Brownfield CQRS part 2 – Command Handlers

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.

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

10 thoughts on “Brownfield CQRS part 2 – Command Handlers

  1. 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.

  2. 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.

  3. 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

  4. 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.

  5. I got lost here. How is the webservice supposed to invoke the command handler? Directly? Inside a Webservice Method?

    public void BookTable(BookTableCommand c)
    {
    var handler = new BookTableCommandHandler()
    handler.Handle(command);
    }

  6. I just realized this is a 2010 article. Is it still a good practice to do wcf with cqrs or is there better ways to do this nowadays? NServiceBus maybe?

Comments are closed.