In my previous post, I described command DTOs and service methods for booking a table at a restaurant. Now, we just need something to interpret this command, and do something useful with it.
- Brownfield CQRS part 1 – Commands
- Brownfield CQRS part 2 – Command Handlers
- Brownfield CQRS part 3 – Queries, Parameters and Results
- Brownfield CQRS part 4 – Command Dispatcher
To do this, we create a corresponding command handler for each command:
public interface ICommandHandler where T : ICommand
{
void Handle(T command);
}
Command handlers are responsible for:
- Performing any required validation on the command.
- Invoking the domain — coordinating objects in the domain, and invoking the appropriate behaviour on them.
Command handlers are application services, and each execution represents a separate unit of work (e.g. a database transaction). There is only one command handler per command, because commands can only be handled once — they are not broadcast out to all interested listeners like event handlers.
Here’s an example for handling our BookTableCommand. A one-to-one handler/command mapping makes it easy to add/remove features from our service.
public class BookTableCommandHandler : ICommandHandler<BookTableCommand>
{
IDinnerServiceRepository nights;
public void Handle(BookTableCommand command)
{
var dinnerService = nights[command.TimeAndDay];
var party = new Party(command.PartySize, command.PartyName);
night.TakeBooking(party);
}
}
Note each command implements ICommand — a simple explicit role marker interface that also allows us to use constraints on generic types and automate IoC registration of command handlers.
public interface ICommand { }
Command validation and errors
Aside from transient technical faults, there are two business reasons a command might fail:
- The command was not valid — e.g. you tried to book a table for zero people.
- The command could not succeed — e.g. the restaurant is fully booked that night.
Ideally, the client will have pre-checked these to save time, but if the command handler detects a problem, how do we report it back to the user, given commands are not allowed to have return values? How would we report success even?
Actually, this is not a problem at all — commands have no return value, but they can throw a detailed validation/command failed exception back to the client. If they didn’t throw anything, it is assumed to have succeeded.
What if you execute commands asynchronously — e.g. queued and executed at some later time? We can’t throw an exception back to the client in this case. But that’s fine — the client must always assume their asynchronous command will succeed. If it does fail, it will be reported back through some alternate channel (e.g. via e-mail or the query side). This is why it is important to pre-validate commands on the client as much as possible.
Next: Part 3 – Queries, Parameters and Results
June 16, 2010



7 Comments
seagile on June 16, 2010 at 11:32 am.
I know Greg and Udi in particular are very keen on implementing asynchronous commands, but if part of the workflow is getting a confirmation and printing a letter you can hand over to e.g. a patient (so he knows when his appointment is due), then “e-mail” just isn’t good enough of a feedback mechanism. I don’t want to give the patient a call saying his appointment didn’t make into the system because somebody else is already attending at that particular point in time (== the concurrency exception we got in the system asynchronously).
It doesn’t mean the command can’t be executed asynchronously from a technical point of view (I could wait in my UI for a callback/timeout message), but it does mean that this particular workflow is inherently request-response.
What I’m trying to say is that you shouldn’t take a blind leap of faith on the whole asynchronous command thing. Depending on how crucial the user feedback is (the domain expert should know) you’ll be able to choose from request/response or asynchronous processing. Choosing the wrong one could disrupt the user’s workflow and create havoc in your system.
seagile on June 16, 2010 at 11:37 am.
Sidenote: I’m aware of compensating commands/actions, but they suffer the same problem … They are just too late in the feedback cycle for this particular problem.
To be very clear: everything I’m saying here is depending on the context I’m working in. Your situation might differ.
Richard on June 16, 2010 at 11:47 am.
Seagile: I would use a synchronous endpoint in situations like this. Same as example of withdrawing cash at a cash machine.
Pingback: Richard Dingwall » Brownfield CQRS part 3 – Queries, Parameters and Results
Pingback: Richard Dingwall » Brownfield CQRS part 1 – Commands
Kevin Berridge on July 6, 2010 at 8:48 pm.
Where does persistence happen? Typically in DDD the domain doesn’t persist itself, but it looks like in this example night.TakeBooking(party); must be performing whatever persistence is needed. Is that correct?
Also, I couldn’t tell from your series of posts if you have a separate data store for your commands (event sourcing) than for your queries. Could you clarify that?
Thanks,
Kevin
Richard on July 7, 2010 at 10:28 am.
Kevin: these posts don’t go into persistence strategies — first we just need to get the external interfaces right, then you can implement the service internals using full-blown event sourcing etc.