Skip to content

Commit 1a7c898

Browse files
committed
add event broker page to docs
1 parent 3b87333 commit 1a7c898

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
EventBroker
2+
===========
3+
4+
The ``EventBroker`` provides a lightweight publish/subscribe (pub-sub) mechanism for
5+
decoupled event-driven communication between components. It acts as a central
6+
dispatcher that routes events to registered subscribers based on event type.
7+
8+
Overview
9+
--------
10+
11+
The broker is implemented as a singleton, so all parts of the application interact
12+
with the same event registry. Components can:
13+
14+
- **Register** handlers for specific event types
15+
- **Publish** events to notify all interested subscribers
16+
- Rely on a simple recursion guard to prevent runaway event loops
17+
18+
This pattern is useful for cross-cutting concerns such as logging, state updates,
19+
UI notifications, or domain events.
20+
21+
Basic Usage
22+
-----------
23+
24+
Registering Subscribers
25+
~~~~~~~~~~~~~~~~~~~~~~~
26+
27+
Subscribers are callables that accept a single event instance.
28+
29+
.. code-block:: python
30+
31+
from tavi.meta.event.event_interface import Event
32+
from myapp.events import UserCreatedEvent
33+
from myapp.event_broker import EventBroker
34+
35+
def on_user_created(event: UserCreatedEvent) -> None:
36+
print(f"User created: {event.user_id}")
37+
38+
broker = EventBroker()
39+
broker.register(UserCreatedEvent, on_user_created)
40+
41+
Publishing Events
42+
~~~~~~~~~~~~~~~~~
43+
44+
When an event is published, all subscribers registered for that event type are invoked.
45+
46+
.. code-block:: python
47+
48+
event = UserCreatedEvent(user_id="123")
49+
broker.publish(event)
50+
51+
Each subscriber receives a **deep copy** of the event instance. This prevents
52+
subscribers from mutating shared state and affecting other listeners.
53+
54+
Event Dispatch Semantics
55+
------------------------
56+
57+
- **Dispatch is synchronous**: subscribers are called in the order they were registered.
58+
- **Dispatch is type-based**: only subscribers registered for the exact event class
59+
(``type(event)``) are invoked.
60+
- **Event instances are copied**: each subscriber receives an isolated event object.
61+
62+
Recursion Guard
63+
---------------
64+
65+
The broker enforces a maximum call depth to prevent infinite or runaway recursion
66+
when events trigger other events during handling.
67+
68+
If the maximum depth is exceeded, a ``RuntimeError`` is raised:
69+
70+
.. code-block:: python
71+
72+
RuntimeError: Event recursive depth of 1 has been exceeded.
73+
74+
This protects against patterns like:
75+
76+
- A handler publishing the same event type it is subscribed to
77+
- Circular event chains between handlers
78+
79+
If deeper event chaining is required, the maximum depth can be increased:
80+
81+
.. code-block:: python
82+
83+
broker = EventBroker()
84+
broker.call_depth_max = 3
85+
86+
Recommended Practices
87+
---------------------
88+
89+
- **Keep handlers small and side-effect focused**
90+
Event handlers should perform limited, well-defined actions and avoid complex control flow.
91+
92+
- **Avoid cyclic event dependencies**
93+
Design event flows to be acyclic where possible. The recursion guard is a safety net,
94+
not a control mechanism.
95+
96+
- **Prefer domain-specific events**
97+
Use narrowly scoped event types (e.g., ``UserCreatedEvent`` instead of a generic
98+
``UserEvent``) to keep subscriptions explicit and predictable.
99+
100+
- **Do not mutate incoming events**
101+
Although handlers receive copies, treat events as immutable to preserve intent
102+
and make behavior easier to reason about.
103+
104+
Typical Use Cases
105+
-----------------
106+
107+
- Emitting domain events from application services
108+
- Triggering side effects such as logging, metrics, or notifications
109+
- Decoupling UI updates from core business logic
110+
- Broadcasting lifecycle events (startup, shutdown, state changes)
111+
112+
Limitations
113+
-----------
114+
115+
- No built-in support for asynchronous handlers
116+
- No wildcard or base-class subscriptions (exact type matching only)
117+
- No unregistration mechanism for subscribers
118+
- No event classification system. It does not validate that a subscriber *should* receive a specific event class. (Model vs Presenter)
119+
- Global singleton scope may be undesirable in some testing or multi-tenant contexts
120+
121+
For more complex workflows (async dispatch, filtering, prioritization, or scoped
122+
brokers), consider layering a more advanced event bus on top of this interface.

0 commit comments

Comments
 (0)