Command Handlers

The key responsibility of the Application Service is to orchestrate the activities that can be performed with the Domain Model. It is common that a single Application Service is defined for a single Use Case (Greg Young).

The Application Service is also where there is danger in growing an Anemic Domain Model. While this is an anti-pattern if your intent is to grow a traditional Domain Model, it isn’t an anti-pattern if your intent is to always use a simple Transaction Scripts over simple Data Objects.

An Anemic Domain model does become anti-pattern when complex changes are requested after the team commits to their decision - it doesn’t scale well, and it’s at this point that they regret not being able to adapt to the new complexity that gets introduced down the road (Greg Young).

This concept is tough, not only to comprehend quickly, but also to put into words and explain. However, as I’ve said before, I plan to go into much more detail on my blog and further learning resources. For right now, we’re going to talk quickly about what an Application Service becomes in a CQRS architecture - a Command Handler.

It’s valuable to show what an Application Service would look like before we started thinking about Command Handlers, since Command Handlers are a refactoring of the traditional Application Service.

The traditional Application Service that would handle a CreateRoastSchedule might look like this:

public class RoastScheduleApplicationService {
    private readonly IRepository<RoastSchedule> _repository;

    public RoastScheduleApplicationService(IRepository<RoastSchedule> repository) {
        _repository = repository;
    }

    public RoastSchedule CreateRoastSchedule() {
        var roastSchedule = new RoastSchedule(Guid.NewGuid());
        _repository.Save(roastSchedule);
        return roastSchedule;
    }
}

There are a couple things to note here. One, notice how we are returning the persisted RoastSchedule object for use in our web page that is going to show the newly created Roast Schedule for the user. We’d probably map it to a View Model or a DTO. With CQRS, we handle this a different way.

Let’s look at another potential Application Service:

public class UpdateRoastScheduleRoastDaysApplicationService {
    private readonly IRepository<RoastSchedule> _repository;

    public UpdateRoastScheduleRoastDaysApplicationService(IRepository<RoastSchedule> repository) {
        _repository = repository;
    }

    public RoastSchedule UpdateRoastScheduleRoastDays(Guid roastScheduleId, List<RoastDay> newRoastDays) {
        var roastSchedule = _repository.GetById(roastScheduleId.ToString());
        roastSchedule.UpdateRoastScheduleRoastDays(newRoastDays);
        _repository.Save(roastSchedule);
        return roastSchedule;
    }
}

Application Service to Command Handler

To show you how we go from Application Services to Command Handlers, let’s apply a Introduce Parameter Object refactoring.

public class UpdateRoastScheduleRoastDaysApplicationService {
    private readonly IRepository<RoastSchedule> _repository;

    public UpdateRoastScheduleRoastDaysApplicationService(IRepository<RoastSchedule> repository) {
        _repository = repository;
    }

    // parameters have been extracted into a class called UpdateRoastScheduleDays
    public RoastSchedule UpdateRoastScheduleRoastDays(UpdateRoastScheduleRoastDays u) {
        var roastSchedule = _repository.GetById(u.RoastDayId.ToString());
        roastSchedule.UpdateRoastScheduleRoastDays(u.NewRoastDays);
        _repository.Save(roastSchedule);
        return roastSchedule;
    }

    // previously parameters to UpdateRoastScheduleRoastDays
    public class UpdateRoastScheduleRoastDays {
        private readonly Guid RoastDayId;
        private readonly List<RoastDay> NewRoastDays;
        public UpdateRoastScheduleRoastDays(Guid roastDayId, List<RoastDay> newRoastDays) {
            RoastDayId = roastDayId;
            NewRoastDays = newRoastDays;
        }
    }
}

This refactoring could be done to all Application Service methods in an app. It allows all of our Application Service methods to have a single parameter. This translates to the ability for us to find a common interface among these methods. This common interface is our Command Handler.

If we focus on the extracted Parameter Object UpdateRoastScheduleRoastDays, we notice that this is simply a Message:


public class CreateRoastSchedule {
    public readonly RoastScheduleId RoastScheduleId;
    public CreateRoastSchedule(RoastScheduleId roastScheduleId) {
        RoastScheduleId = roastScheduleId;
    }
}

public class UpdateRoastScheduleRoastDays {
    private readonly Guid RoastDayId;
    private readonly List<RoastDay> NewRoastDays;
    public UpdateRoastScheduleRoastDays(Guid roastDayId, List<RoastDay> newRoastDays) {
        RoastDayId = roastDayId;
        NewRoastDays = newRoastDays;
    }
}

Messages can be Commands or Events. Let’s write marker interfaces for them:

public interface Message { }
public class Command : Message { }
public class Event : Message { }

UpdateRoastScheduleRoastDays and CreateRoastSchedule are both something we can say “No” to. They tell our our Domain to do something. This makes them Commands.

public class CreateRoastSchedule : Command {
	...
}

public class UpdateRoastScheduleRoastDays : Command {
	...
}

Now what about our Application Service’s Domain Model orchestration?… i.e. this code:

public RoastSchedule CreateRoastSchedule() {
    var roastSchedule = new RoastSchedule(Guid.NewGuid());
    _repository.Save(roastSchedule);
    return roastSchedule;
}

//...

public RoastSchedule UpdateRoastScheduleRoastDays(Guid roastScheduleId, List<RoastDay> newRoastDays) {
    var roastSchedule = _repository.GetById(roastScheduleId.ToString());
    roastSchedule.UpdateRoastScheduleRoastDays(newRoastDays);
    _repository.Save(roastSchedule);
    return roastSchedule;
}

Since these, and all other methods now only take a single Parameter Object that implements Command, we can refactor them to have a common Interface: ICommandHandler<TCommand>.

public interface ICommandHandler<TCommand> where TCommand : Message {
    void Handle(TCommand message);
}

Our original CreateRoastScheduleApplicationService becomes CreateRoastScheduleCommandHandler:

public class CreateRoastScheduleCommandHandler : ICommandHandler<CreateRoastSchedule> {
    private readonly IRepository<RoastSchedule> _repository;

    public CreateRoastScheduleCommandHandler(IRepository<RoastSchedule> repository) {
        _repository = repository;
    }

    public void Handle(CreateRoastSchedule message) {
        var roastSchedule = new RoastSchedule(Guid.NewGuid());
        _repository.Save(roastSchedule);
    }
}

And our UpdateRoastScheduleRoastDaysApplicationService becomes UpdateRoastScheduleRoastDaysCommandHandler:

public class UpdateRoastScheduleRoastDaysCommandHandler : ICommandHandler<UpdateRoastScheduleRoastDays> {
    private readonly IRepository<RoastSchedule> _repository;

    public UpdateRoastScheduleRoastDaysApplicationService(IRepository<RoastSchedule> repository) {
        _repository = repository;
    }

    // parameters have been extracted into a class called UpdateRoastScheduleDays
    public void Handle(UpdateRoastScheduleRoastDays message) {
        var roastSchedule = _repository.GetById(message.RoastDayId.ToString());
        roastSchedule.UpdateRoastScheduleRoastDays(message.NewRoastDays);
        _repository.Save(roastSchedule);
    }
}

In case you didn’t notice, our Handle method returns void now. This is because of our switch to the Command Query Responsibility Segregation (CQRS) pattern. I’ll talk much more about this as we continue…


continue reading…

previous…


I will be releasing most of this content only to subscribers. Make sure you sign up by clicking the big red button!


Related Posts:

Tweet
comments powered by Disqus