Dispatcher Execution Routing
Relevant source files
The following files were used as context for generating this wiki page:
The Dispatcher Execution Routing system in FLEXT-Core provides a centralized mechanism for orchestrating message-based communication. It serves as an application-level command bus that routes Command, Query, and Event messages to their appropriate handlers using a combination of explicit registration, protocol discovery, and auto-discovery patterns.
The system is designed to ensure decoupling between message producers and consumers, providing a unified dispatch interface for point-to-point communication and a publish interface for one-to-many event broadcasting. It leverages railway-oriented programming (ROP) to return Result objects, ensuring that routing failures or execution errors are handled as data rather than unmanaged exceptions.
Sources: src/flext_core/dispatcher.py:15-20, examples/ex_04_flext_dispatcher.py:112-118, tests/unit/test_dispatcher.py:11-15
Architecture and Core Components
The architecture centers on the FlextDispatcher class, which manages three distinct internal registries for routing logic.
Dispatcher Components
| Component | Description |
|---|---|
Handler Registry (_handlers) | Maps unique route names to a tuple of the handler instance and its resolved callable. |
Auto-Handler Registry (_auto_handlers) | A sequence of handlers that do not have a fixed route but can dynamically determine if they can process a message type. |
Event Subscribers (_event_subscribers) | A multi-map of route names to lists of subscribers for event broadcasting. |
Sources: src/flext_core/dispatcher.py:24-40
Message Flow Overview
The following diagram illustrates the high-level flow of a message through the dispatcher, from resolution to execution.
A message must satisfy the p.Routable protocol, which typically implies it has attributes or a structure that the dispatcher can use to derive a string-based "route name."
Sources: src/flext_core/dispatcher.py:44-55, tests/unit/test_dispatcher.py:24-32
Handler Registration and Discovery
Handlers can be registered using the register_handler method. The system supports various handler shapes, including classes implementing specific protocols or simple callables.
Supported Handler Protocols
The dispatcher automatically detects the execution method based on the following protocol priority:
- DispatchMessage: Methods named
dispatch_message. - Handle: Methods named
handle. - Execute: Methods named
execute. - Callable: Any object that is
callable(e.g., a function or a class with__call__).
Sources: src/flext_core/dispatcher.py:108-124, examples/ex_04_flext_dispatcher.py:11-37
Route Discovery Logic
The dispatcher determines the "route name" for a handler using a tiered lookup:
- Explicit Attribute: Checks for a
message_typeattribute on the handler instance. - Type Hinting: Inspects the handler's signature to compute accepted message types.
- Auto-Discovery: If no explicit route is found and the handler implements
p.AutoDiscoverableHandler, it is moved to the auto-discovery registry.
Dispatching and Publishing
The dispatcher provides two primary modes of execution: synchronous point-to-point dispatching and event-based publishing.
Synchronous Dispatch
The dispatch method is used for Commands and Queries. It ensures that exactly one handler processes the message.
- Logic: It first attempts a lookup in the primary
_handlersregistry. If not found, it iterates through_auto_handlersand callscan_handle(msg_type)on anyAutoDiscoverableHandler. - Error Handling: If no handler is resolved, it returns a failure result with the operation "resolve message handler".
Sources: src/flext_core/dispatcher.py:44-63, tests/unit/test_dispatcher.py:65-75
Event Publishing (Fan-out)
The publish method handles event distribution to multiple subscribers.
- Sequential Execution: It resolves all matching subscribers from both the explicit
_event_subscribersregistry and the auto-discovery registry. - Sequence Support: The method can accept a single event or a sequence (list/tuple) of events, processing them iteratively.
- No-op Success: Publishing an event with no subscribers is treated as a successful no-op.
Sources: src/flext_core/dispatcher.py:65-88, tests/unit/test_dispatcher.py:136-150
Execution Mechanics
Actual handler execution is delegated to an internal utility to ensure consistent logging and error wrapping.
Execution Wrapper
The method _execute_handler (calling execute_dispatcher_handler) wraps the handler call.
This wrapper ensures that:
- Execution is logged with the route name.
- Any exceptions raised within the handler are caught and transformed into a
Result.fail_opto maintain railway consistency.
Sources: src/flext_core/dispatcher.py:165-173, src/flext_core/_utilities/dispatcher_execute.py:11-20
Summary of Dispatcher APIs
| Method | Purpose | Key Parameters | Return Type |
|---|---|---|---|
dispatch | Routes a Command/Query to a single handler. | message: p.Routable | p.Result[t.JsonPayload] |
publish | Broadcasts an Event to all subscribers. | event: p.Routable | Sequence | p.Result[bool] |
register_handler | Adds a handler to the registry. | handler, is_event: bool | p.Result[bool] |
Sources: src/flext_core/dispatcher.py:44, src/flext_core/dispatcher.py:65, src/flext_core/dispatcher.py:91
The Dispatcher Execution Routing system provides a robust foundation for building Clean Architecture applications by strictly separating message definition from execution logic, supported by a flexible registration system that accommodates both explicit routing and dynamic discovery.