What does a command handler do?
A command handler receives a command and brokers a result from the appropriate aggregate. "A result" is either a successful application of the command, or an exception.
This is the common sequence of steps a command handler follows:
- Validate the command on its own merits.
- Validate the command on the current state of the aggregate.
- If validation is successful, 0..n events (1 is common).
- Attempt to persist the new events. If there's a concurrency conflict during this step, either give up, or retry things.
Should a command handler affect one or several aggregates?
Do I put logic in command handlers?
Yes. Exactly what logic depends on your factoring.
The logic for validating the command on its own merits always goes in the command handler. If the command handler is just a method on the aggregate, then the next step is simply to use the state of the aggregate to do further validation. In a more functional factoring, where the aggregate exists independently of the command handlers, the next step would be to load the aggregate and do validation against it.
Provided validation is successful, the command handler should then produce events. Depending on the factoring, it may also take a further step to try and persist them.
In the Edument CQRS starter kit, command handlers are methods that return events. The loading of events, building up of the aggregate, and persisting of events is completely factored out of command handlers. This keeps them very clean and focused, and thus completely decoupled from persistence mechanisms.
However you have it, the logic boils down to validation and some sequence of steps that lead to the command becoming an exception or event(s). If you're tempted to go beyond this, see the rest of the questions in this section.
Can I call a read side from my command handler?
Can I do logging, security, or auditing in my command handlers?
Yes. The decorator pattern comes in handy here to separate those concerns neatly.
How are conflicts between concurrent commands handled in the command handler?
The place where the new events for the aggregate are persisted is the only place in the system where we need to worry about concurrency conflicts. The event store knows the sequence number of the latest event applied on that aggregate, and the command handler knows the sequence number of the last event it read. If these numbers do not agree, it means some other thread or process got there first. The command handler can then load up the events again and make a new attempt.
Should I do things that have side-effects in the outside world (such as sending email) in a command handler?
No, since a concurrency conflict will mean the command handler logic will be run again. Do such things in an event handler.