Skip to content

Observer Pattern

Overview

The Observer pattern establishes a one-to-many dependency between objects, enabling automatic notification of all dependent objects when an observed object changes state. In NERV, this pattern forms the foundation of the event system and reactive programming model.

Key Concepts

  • Subject/Observable: The object being observed that maintains a list of observers
  • Observer: Objects that receive notifications when the subject changes
  • Notification Mechanism: Method for communicating state changes
  • Registration/Unregistration: Dynamic observer management

Benefits

  • Loose Coupling: Subjects and observers can vary independently
  • Broadcast Communication: One-to-many notification without specifying receivers
  • Dynamic Relationships: Observers can be added or removed at runtime
  • Open/Closed Principle: New observers can be added without modifying subject

Implementation Considerations

  • Memory Management: Prevent memory leaks from forgotten observer references
  • Notification Ordering: Consider the order in which observers are notified
  • Notification Granularity: Balance between fine-grained and coarse updates
  • Thread Safety: Ensure thread-safe observer registration and notification
  • Observer Lifecycle: Handle observers that may be destroyed during notification

Core Interface

Observable Protocol

The Observable protocol defines objects that can emit events to registered observers:

  • add_observer(observer: Callable[[E, Any], None]) -> Callable[[], None]: Adds an observer callback function and returns an unsubscribe function for easy cleanup.

  • remove_observer(observer: Callable[[E, Any], None]) -> None: Removes a previously registered observer from the notification list.

  • notify(event: E, data: Any = None) -> None: Notifies all registered observers about an event, passing event information and optional data.

Pattern Variations

Pull-Based Observer

Observers retrieve the state they need from the subject when notified, minimizing data coupling.

Push-Based Observer

Subject sends all relevant state data to observers, simplifying observer implementation.

Event-Stream Observer

Observers subscribe to specific event types in a stream, allowing fine-grained filtering.

Integration in NERV

The Observer pattern is primarily implemented in the EventBus component, which serves as the foundation for the Reactive Event Mesh pattern. It enables:

  • Decoupled Communication: Components interact without direct references
  • System-Wide Events: Broadcasting state changes across the architecture
  • Extensibility: Adding new behavior by attaching observers
  • Multi-Level Notification: Event propagation through the system

Implementation Details

Observable Implementation Strategies

A typical Observable implementation includes:

  1. Thread-Safe Observer Collection:

    • Uses a synchronized collection or lock to prevent concurrent modification issues
    • Maintains a list/set of observer callback functions
  2. Unsubscribe Token Generation:

    • Returns a function that removes the observer when called
    • Enables easy cleanup and prevents memory leaks
  3. Copy-on-Read Pattern:

    • Makes a copy of the observer list before notification
    • Prevents issues if observers are added/removed during notification
  4. Error Handling:

    • Catches exceptions from individual observers
    • Continues notifying remaining observers if one fails
    • Logs errors but prevents them from affecting other observers

Usage in NERV Components

  • EventBus: Central implementation of the Observable pattern
  • Agent Controller: Observes and coordinates multiple worker agents
  • Stream Controllers: Notify subscribers of streaming state changes
  • Resource Managers: Track and broadcast resource state changes
  • Workflow Engine: Notify components of workflow state transitions
  • Publish-Subscribe Pattern
  • Mediator Pattern
  • Event-Driven Architecture

Released under the MIT License.