Connect with Amin Boulouma Official
The Event Dispatcher: Decoupling via Multicast
In complex systems, components often need to communicate without being tightly bound to one another. Hard-coding dependencies (e.g., ModuleA calling ModuleB directly) creates a fragile, rigid architecture. The Observer Pattern, implemented here as an EventDispatcher, provides a clean solution: a centralized “broker” that manages communication between producers and consumers.
The Problem: Tight Coupling
When your Git task logic must directly call your UI logic, your database logger, and your notification handler, every component becomes “aware” of every other component. If you change one, you risk breaking all.
The Solution: The Multicast Observer Pattern
The EventDispatcher acts as a middleware. Producers “dispatch” events to the broker, and “subscribers” listen for events they care about. The producer doesn’t know who, or even if, anyone is listening.
Implementing the Broker
class EventDispatcher:
"""An event broker implementing a standard multicast Observer pattern."""
def __init__(self) -> None:
self._listeners: Dict[str, List[Callable[[Any], None]]] = {}
def subscribe(self, event_type: str, handler: Callable[[Any], None]) -> None:
if event_type not in self._listeners:
self._listeners[event_type] = []
self._listeners[event_type].append(handler)
def dispatch(self, event_type: str, event_data: Any = None) -> None:
handlers = self._listeners.get(event_type, [])
if not handlers:
logger.warning(f"Event '{event_type}' has no active subscribers.")
return
for handler in handlers:
try:
handler(event_data)
except Exception as err:
logger.error(f"Error in event '{event_type}': {err}")
Why Decoupling Matters
- Independent Scalability: You can add a new logger or a new analytics module by simply subscribing to an event, without modifying the existing business logic.
- Fault Isolation: The
try-exceptblock inside thedispatchloop ensures that a crashing subscriber does not bring down the entire event-dispatching process. - Extensibility: The system becomes a “plug-and-play” architecture where you can attach new functionality at runtime.
Best Practices
- Keep Dispatcher Logic Simple: The broker should only route traffic. Avoid adding complex business logic inside the
dispatchloop itself. - Error Resilience: Always wrap subscriber calls in
try...exceptblocks. If one listener fails, it should never prevent other listeners from receiving the event. - Avoid Infinite Loops: Be careful not to dispatch an event in a handler that listens for that same event, as this creates a recursive “event storm.”
The EventDispatcher transforms your architecture from a rigid tree into a flexible, communicative network. It is the foundation for building systems that are truly modular and capable of evolving alongside your requirements.