Domain Command Patterns - Handlers
In the last post, we looked at validation patterns in domain command handlers in response to a question, "Command objects should [always/never] have return values". This question makes an assumption - that we have command objects!
In this post, I want to look at a few of our options for handling domain commands:
When I look at command handling, I'm really talking about the actual "meat" of the request handling. The part that mutates state. In very small applications, or very simple ones, I can put this "mutation" directly in the request handling (i.e., the controller action or event handler for stateful UIs).
But for most of the systems I build, it's too much to shove this all in the edge of my application. This raises the question - where should this logic go? We can look at a number of design patterns (including the Command Object pattern). Ultimately, I have a block of code that mutates state, and I need to decide where to put it.
Static Helper/Manager/Service Functions
A very simple option would be to create some static class to host mutation functions:
public static class SomethingManager {
public static void DoSomething(SomethingRequest request,
MyDbContext db) {
// Domain logic here
}
}
If our method needed to work with any other objects to do its work, these would all be passed in as method arguments. We wouldn't use static service location, as we do have some standards. But with this approach, we can use all sorts of functional tricks at our disposal to build richness around this simple pattern.
How you break up these functions into individual separate classes is up to you. You might start off with a single static class per project, static class per domain object, per functional area, or per request. The general idea is that although C# doesn't support the full functional richness of F#, static functions provide a reasonable alternative.
The advantage to this approach is it's completely obvious exactly what the logic is. The return type above is "void" but as we saw with the validation options, it could be some sort of return object as well.
DDD Service Classes
Slightly different than the static class is the DDD Service Pattern. The big difference is that the service class is instance-oriented, and often uses dependency injection. The other big difference is in the wild I typically see service classes more entity or aggregate oriented:
public class SomethingService : ISomethingService {
public SomethingService(MyDbContext db) {
_db = db;
}
public void DoSomething(SomethingRequest request) {
// Domain logic here
}
}
Services in the DDD world should be designed around a coordination activity. After all, the original definition was that services are designed to coordinate between aggregates, or between aggregates and external services. But that's not what I typically see, I typically see Java Spring-style DDD services where we have an entity Foo, and then:
- FooController
- FooService
- FooRepository
I would highly discourage these kinds of services, as we've introduced arbitrary layering without much value. If we're doing DDD right, services would be a bit rarer, and therefore not needed for every single command in our system.
Request-Specific Handlers
With both the service and manager options, we typically see multiple requests handled by multiple method inside the same class. Although there's nothing stopping you from creating a service per request, the request-specific handler achieves this same end goal: a single class and method handling each individual request.
I copied this pattern enough times where I finally extracted the code into a library, MediatR. We create a class to encapsulate handling each individual request:
public class SomethingRequestHandler : IRequestHandler<SomethingRequest> {
public void Handle(SomethingRequest request) {
}
}
There are variants for handling a request: sync/async, return value/void and combinations thereof.
This tends to be my default choice for handling domain commands, as it encourages me to isolate the logic from each request from any other request.
But sometimes the logic in my handler gets complicated, and I want to push that behavior down.
Domain Aggregate Functions
Finally, we can push our behavior down directly into our aggregates:
public class SomethingAggregate {
public void DoSomething(SomethingRequest request) {
}
}
Or, if we don't want to couple our aggregates to the external request objects, we can destructure our request object into individual values:
public class SomethingAggregate {
public void DoSomething(string value1, int value2, decimal value3) {
}
}
In my systems, I tend to start with the simple, procedural code inside a handler. When that code exhibits code smells, I push the behavior down into my domain objects. Of course we can just do that by default - only reserve procedural code for my CRUD areas of the application.
This certainly isn't an exhaustive list of domain command patterns, but it's 99% of what I typically see. I can mix multiple choices here as well - a handler for the logic to load/save, and a domain function for the actual "business logic".
I'm ignoring the actual Command Object pattern, as I find it might fit well with UI-level commands, it doesn't fit well with domain-level commands.
We can mix our validation choices too, and have field validation done by a framework, domain validation done by our aggregates, and use domain aggregate functions that return "result" objects.
So which way is "best"? I can't really say, a lot of this is a judgement call by your team. But with several options on the table we can at least make an informed decision.