CQRS, Events, and Sagas Component
1. Overview (What does this do?)
The CQRS (Command Query Responsibility Segregation) component provides integrated patterns for separating read operations (Queries) from write operations (Commands). It also provides an Event Dispatcher for Event-Driven Architectures and supports Sagas for managing distributed transactions across multiple microservices or database boundaries.
2. Philosophy (Why does it exist?)
In complex Enterprise architectures and microservices, having Controllers directly call Repositories to mutate state leads to tightly coupled, hard-to-maintain code. By forcing mutations through a Command Bus and reads through a Query Bus, ferrox-py enforces a clear separation of concerns. This allows read paths to be optimized (e.g., using caching or read replicas) entirely independently of the write paths, and enables reactive event-driven flows.
3. Target Audience (Who is it for?)
This component is designed for advanced architects and developers building complex, highly scalable systems. It is specifically aimed at those implementing Domain-Driven Design (DDD) and those who need to orchestrate complex business transactions that span multiple services without relying on distributed two-phase commits.
4. Architecture (How does it work?)
- Command/Query Bus: Resolves incoming Commands/Queries to their registered Handlers.
- Event Dispatcher: An in-memory Pub/Sub bus where Publishers emit Events (e.g.,
PaymentCompleted) and Subscribers asynchronously react to them. It is designed to be easily extensible to external message brokers like Redis Pub/Sub or RabbitMQ. - Sagas: A state machine engine that executes a sequence of local transactions. If one step fails, the Saga orchestrator automatically triggers compensating actions (rollbacks) for all previously successful steps.
5. Installation / Setup
The in-memory CQRS and Event buses are included natively in ferrox-py. For distributed messaging (e.g., RabbitMQ or Redis), additional specific driver packages must be installed and configured within the IoC Container.
6. Quickstart (Usage)
from ferrox_py.cqrs.bus import CommandBus
# 1. Define the Command
class CreateOrderCommand:
def __init__(self, item_id: str):
self.item_id = item_id
# 2. Define the Handler logic (mocked)
class OrderService:
def create_order(self, cmd: CreateOrderCommand):
print(f"Order created for item {cmd.item_id}")
return True
# 3. Registration and Dispatch
bus = CommandBus()
order_service = OrderService()
# Register the handler that knows how to process the Command
bus.register_handler(CreateOrderCommand, order_service.create_order)
# The API Controller simply dispatches the command
result = bus.dispatch(CreateOrderCommand(item_id="12345"))
7. Ecosystem Integration
CQRS integrates heavily with the Data Component (for actual persistence executed by the Handlers) and the Pipes/Interceptors. Specifically, a Validation Pipe is often attached to the Command Bus to ensure that every Command object is structurally valid before it ever reaches the Business Service layer.