CQRS.NET
2.1
A lightweight enterprise framework to write CQRS, event-sourced and micro-service applications in hybrid multi-datacentre, on-premise and Azure environments.
|
An ICommandHandler<TAuthenticationToken,TCommand> receives an ICommand<TAuthenticationToken> and brokers a result from the appropriate IAggregateRoot<TAuthenticationToken>. "A result" is either a successful application of the command, or an exception. This is the common sequence of steps an ICommandHandler<TAuthenticationToken,TCommand> might follow: More...
Additional Inherited Members | |
Public Member Functions inherited from Cqrs.Messages.IMessageHandler< TCommand > | |
void | Handle (TMessage message) |
An ICommandHandler<TAuthenticationToken,TCommand> receives an ICommand<TAuthenticationToken> and brokers a result from the appropriate IAggregateRoot<TAuthenticationToken>. "A result" is either a successful application of the command, or an exception. This is the common sequence of steps an ICommandHandler<TAuthenticationToken,TCommand> might follow:
Validate the ICommand<TAuthenticationToken> on its own merits. Ask an IAggregateRoot<TAuthenticationToken> to handle the ICommand<TAuthenticationToken>. If validation is successful, 0..n IEvent<TAuthenticationToken> artefacts (1 is common) are queued for publishing. Attempt to persist the new IEvent<TAuthenticationToken> artefacts. If there's a concurrency conflict during this step, either give up, or retry things. Dispatch the queued IEvent<TAuthenticationToken> artefacts.
Should a ICommandHandler<TAuthenticationToken,TCommand> affect one or several IAggregateRoot<TAuthenticationToken>s?
Only one.
Do I put logic in ICommandHandler<TAuthenticationToken,TCommand>?
Yes. Exactly what logic depends on your factoring. The logic for validating the ICommand<TAuthenticationToken> on its own merits always gets executed in the ICommandHandler<TAuthenticationToken,TCommand>, although we recommend refactoring these into an ICommandValidator<TAuthenticationToken,TCommand>. Provided validation is successful we recommend a more functional factoring, where the IAggregateRoot<TAuthenticationToken> exists independently of the ICommandHandler<TAuthenticationToken,TCommand> and the next step would be to load the IAggregateRoot<TAuthenticationToken> from the IUnitOfWork<TAuthenticationToken> and request the IAggregateRoot<TAuthenticationToken> handle the ICommand<TAuthenticationToken> itself. The IUnitOfWork<TAuthenticationToken> should then have uncommitted IEvent<TAuthenticationToken> artefacts as a results of asking the IAggregateRoot<TAuthenticationToken> to handle the ICommand<TAuthenticationToken>. Finally the ICommandHandler<TAuthenticationToken,TCommand> should instruct the IUnitOfWork<TAuthenticationToken> to IUnitOfWork<TAuthenticationToken>.Commit all uncommited IEvent<TAuthenticationToken> artefacts.
However you have it, the logic boils down to validation and some sequence of steps that lead to the ICommand<TAuthenticationToken> becoming an Exception or IEvent<TAuthenticationToken>(s). If you're tempted to go beyond this, see the rest of the remarks.
Can I call a read side (such as a read store, data store or IRepository<TAuthenticationToken>) from my ICommandHandler<TAuthenticationToken,TCommand>?
No.
Can I do logging, security, or auditing in my ICommandHandler<TAuthenticationToken,TCommand>?
Yes. The decorator pattern comes in handy here to separate those concerns neatly.
How are conflicts between concurrent ICommand<TAuthenticationToken>s handled in the ICommandHandler<TAuthenticationToken,TCommand>?
The place where the new IEvent<TAuthenticationToken> artefacts for the IAggregateRoot<TAuthenticationToken> are persisted is the only place in the system where we need to worry about concurrency conflicts. The IEventStore<TAuthenticationToken> knows the sequence number of the latest IEvent<TAuthenticationToken> applied on that IAggregateRoot<TAuthenticationToken>, and the ICommandHandler<TAuthenticationToken,TCommand> knows the sequence number of the last IEvent<TAuthenticationToken> it read. If these numbers do not agree, it means some other thread or process got there first. The ICommandHandler<TAuthenticationToken,TCommand> 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) directly in a ICommandHandler<TAuthenticationToken,TCommand>?
No, since a concurrency conflict will mean the ICommandHandler<TAuthenticationToken,TCommand> logic will be run again. Do such things in an Apply IEvent<TAuthenticationToken> method in an IAggregateRoot<TAuthenticationToken>.
Also see http://cqrs.nu/Faq/command-handlers.
TCommand | : | ICommand<TAuthenticationToken> |