Command Handlers
Command handlers are responsible for processing commands. They encapsulate the logic required to handle a command and ensure that the appropriate actions are taken within the domain model.
Command handlers typically reside in the application layer and serve as a bridge between the application's API and the domain model.
Facts
Command handlers are connected to an aggregate.
Command handlers are always connected to a single aggregate. One command handler per aggregate is the norm, with all aggregate commands handled within it.
A Command handler contains multiple handlers.
Each method in a command handler is connected to a specific command to handle and process.
Handlers are single-purpose.
Each handler method is responsible for handling a single type of command. This ensures that the command handling logic is focused and manageable.
Handlers should deal only with the associated aggregate.
Methods in a command handler should only deal with managing the lifecycle of the aggregate associated with it. Any state change beyong an aggregate's boundary should be performed by eventual consistency mechanisms, like raising an event and consuming it in the event handler of the other aggregate.
Command handlers invoke domain logic.
Command handlers do not contain business logic themselves. Instead, they invoke methods on aggregates or domain services to perform the necessary actions.
Command handlers coordinate actions.
Command handlers coordinate multiple actions related to each command. Primarily, this involves hydrating (fetching) the aggregate, invoking methods to perform state changes and persisting changes through a repository.
Command handlers can return values.
When processed synchronously, command handlers can return values to the caller. This is useful for scenarios like authentication where you need to return a token, or when you need to return the ID of a newly created resource.
Unlike event handlers (which can have multiple handlers for a single event and don't return values), a command can only be handled by a single handler, allowing the return value to be passed back to the caller. This is consistent with the command pattern, where a command represents an intent to perform an action and may need to provide immediate feedback.
By default, command handlers return the position of the command in the command stream back to the caller.
Handler methdos are enclosed in Unit of Work context.
Each handler method is automatically enclosed within a Unit of Work context. This means that all interactions with the infrastructure is packaged into a single transaction. This makes it all the more important to not mix multiple responsibilities or aggregates when handling a command.
Commands can be handled asynchronously.
While handling commands synchronously is the norm to preserve data integrity, it is possible to configure the domain to handle commands asynchronously for performance reasons.
Best Practices
Ensure idempotency.
Command handling should be idempotent, meaning that handling the same command multiple times should not produce unintended side effects. This can be achieved by checking the current state before applying changes.
Handle exceptions gracefully.
Command handlers should handle exceptions gracefully, ensuring that any necessary rollback actions are performed and that meaningful error messages are returned to the caller.
Validate commands.
Ensure that commands are validated before processing. This can be done in a separate validation layer or within the command handler itself.
Return values only when necessary.
Only return values from command handlers when they are genuinely needed by the caller. For example, return authentication tokens or newly created resource IDs, but not entire entity representations. Return values should be small and relevant to the immediate needs of the caller.