Skip to the content.
The Event Dispatcher: Decoupling Systems with the Observer Pattern | AI Systems Design From Scratch

Connect with Amin Boulouma Official

🏠 Documentation Hub 📝 Engineering Blog 💻 GitHub Repository

The Event Dispatcher: Decoupling via Multicast

Amin Boulouma, Software Engineer

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

  1. 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.
  2. Fault Isolation: The try-except block inside the dispatch loop ensures that a crashing subscriber does not bring down the entire event-dispatching process.
  3. Extensibility: The system becomes a “plug-and-play” architecture where you can attach new functionality at runtime.

Best Practices

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.

Connect with Amin Boulouma Official