Request Handlers¶
Overview¶
Request handlers process commands (write operations) and queries (read operations) in your CQRS application. They are the core of your business logic and are automatically resolved through the Dependency Injection container configured in Bootstrap.
| Concept | Description | Can Emit Events |
|---|---|---|
| Commands | Modify system state | ✅ Yes |
| Queries | Read data without side effects | ❌ No (typically) |
| Handlers | Implement business logic, resolved via DI | Depends on type |
Prerequisites
Before creating handlers, ensure you've configured Bootstrap and understand Dependency Injection.
Related Topics
- Stream Handling — For incremental processing
- Chain of Responsibility — For sequential handler chains
- Saga Pattern — For distributed transactions with compensation
- Event Handling — For processing events emitted by command handlers
Request handlers can be divided into two main types:
Command Handler¶
Command Handler executes the received command. The logic of the handler may include, for example, modifying the state of the domain model. As a result of executing the command, an event may be produced to the broker.
| Aspect | Description |
|---|---|
| Purpose | Execute write operations, modify system state |
| Return Value | Optional (can return None or a response) |
| Events | Can emit domain events via events property |
| Handler Type | RequestHandler[Command, Response] or RequestHandler[Command, None] |
Return Value
By default, the command handler does not return any result, but it is not mandatory. You can return a response if needed.
Command Handler Characteristics
- Modifies state: Changes the system's state
- Can emit events: Returns events through
eventsproperty - Idempotent: Should be idempotent when possible
- Transactional: Often wrapped in database transactions
from cqrs.requests.request_handler import RequestHandler
from cqrs.events.event import Event
class JoinMeetingCommandHandler(RequestHandler[JoinMeetingCommand, None]):
def __init__(self, meetings_api: MeetingAPIProtocol) -> None:
self._meetings_api = meetings_api
self.events: list[Event] = []
@property
def events(self) -> typing.List[events.Event]:
return self._events
async def handle(self, request: JoinMeetingCommand) -> None:
await self._meetings_api.join_user(request.user_id, request.meeting_id)
Query Handler¶
Query Handler returns a representation of the requested data, for example, from the read model.
| Aspect | Description |
|---|---|
| Purpose | Read data without modifying system state |
| Return Value | Always returns a response |
| Events | Typically does not emit events |
| Handler Type | RequestHandler[Query, QueryResponse] |
Read Model
The read model can be constructed based on domain events produced by the Command Handler. This allows for optimized read operations.
Query Handler Characteristics
- Read-only: Does not modify system state
- No side effects: Should not have side effects
- Optimized: Can use read models optimized for specific queries
- Fast: Should be fast and efficient
from cqrs.requests.request_handler import RequestHandler
from cqrs.events.event import Event
class ReadMeetingQueryHandler(RequestHandler[ReadMeetingQuery, ReadMeetingQueryResult]):
def __init__(self, meetings_api: MeetingAPIProtocol) -> None:
self._meetings_api = meetings_api
self.events: list[Event] = []
@property
def events(self) -> typing.List[events.Event]:
return self._events
async def handle(self, request: ReadMeetingQuery) -> ReadMeetingQueryResult:
link = await self._meetings_api.get_link(request.meeting_id)
return ReadMeetingQueryResult(link=link, meeting_id=request.meeting_id)