I want to complain about the pattern of using Commands and Command Handlers in this article, but I don’t want to spend my time explaining what they are, so I’ll link you to another blog post. It’s pretty in-depth, so it may take a bit, but what I really like about it is that it’s part of a collection of posts related to the book, Architecture Patterns with Python: Enabling Test-Driven Development, Domain-Driven Design, and Event-Driven Microservices. From what I’ve read in it so far, it’s a lot like the book I was about to write, but a little less in-depth on the product they’re building throughout and also not covering some of the more obscure practices. I’m greatly enjoying it so far, despite largely knowing it all already, so I’m here to plug it. You can purchase it, or you can read it for free on their web site.
So, now I’m probably not going to write that book. I’ll likely still make a series of tutorials or something that I can monetize that tie together in a similar way. But since the majority of it is already covered, I can skim through those topics and link back to their book to save some effort.
Pros of the Pattern
Anyway, moving on. So, why don’t I like the Command Handler pattern? In short, it’s because it’s an abstraction of a function that gives very little benefit. I’ll list the benefits first, so that it doesn’t look like I’m being unfair.
- It’s quite abstract. I technically already said this, but I’m reiterating it. Being more abstract means that it allows for a reusability and repeatability.
- It’s a fairly simple and well-known pattern, making it relatively easy to pick up on for new people on the project.
- It’s easy to organize. You can place all of your Commands (and maybe handlers, too) in one area and that shows to new people what use cases are available. If you place the handlers in the same or similar location, it makes it easy to see which handlers have already been handled and how they’re implemented.
- Because it’s so big and obvious, it helps force you into a good pattern. A lot of other patterns are too easy to ignore, leading to going of in a different direction and making spaghetti code.
Cons of the Pattern
So, with all these benefits, what could I possibly have against it?
The primary reason is that it’s overengineered, in my opinion. You only need one part of the pattern (the handlers) to largely accomplish all of the above. It’s slightly less effective at it, but I feel like the trade-off actually provides more clarity and makes it easier to grok the codebase.
Before moving on to what I think is generally a better idea, I’ll give you another reason why I’m not a fan: it’s too dynamic. You dynamically register Commands with their Handlers on the Queue. If you don’t register the Commands and Handlers, you can have all the other building blocks in place and it could still fail. And because it’s a dynamic registration, even a type-aware compiler can’t help you know that you don’t have everything in place. This means that it’s possible to write a web router that adds the proper Command onto the Queue and have a proper Command Handler written for that Command, but it still won’t run. Presumably, you have something written for the Queue to alert you when a Command type isn’t registered when one comes through, but that’s a runtime solution. This is the same problem I have with dependency injection frameworks.
Along with that, it’s an extra step at runtime, checking the type of the Command and looking up the Handler instead of just calling the Handler directly. It’s even possible to accidentally register them incorrectly and, in a dynamic language, have nothing to ensure that it would even work.
What Do I Suggest Instead?
So what’s my alternative? Well, it largely already exists in the as the basic service layer in DDD. The service layer provides you with all the actions you’re allowed to use on the domain. Instead of creating the Commands and Command Handlers that (often) simply redirect to the service layer functions, just break all those service layer functions into their own Action objects. That’s not an official term; I’m just going to refer to them that way for the rest of the post.
These Action objects encapsulate both the Commands and Command Handlers into single objects. By doing so, at a minimum, you can at least stop naming everything
FooHandler. You just name it the “Foo” part. If you really want, you can add a “Command” (which could be confused with the other pattern), “Action” (which is kind of superfluous), or “Service” (possibly the best option, though it will tend to lead people away from the best naming pattern for the “Foo” part) suffix, but I suggest against that.
Anyway, Actions can take in the dependencies that the Command Handler uses via a constructor, and has a function (or IS a function, if that function is
__call__(...)) that takes all the necessary information as parameters instead of a Command object (which incidentally, is really just an abstraction of a parameter list).
What about the Command Queue? It has extra scalability benefits, made possible by the Command pattern, right? Sure, the Command pattern makes the Queue pretty simple to build, but it’s still actually possible to use a queue with Actions. And with Actions, you don’t need to register anything. You’re effectively just adding functions to the queue that can be run later. You just need to have a way to give it all the parameters without running right away. A
functools.partial can take care of that, or some other kind of function that will run the Action later. You could even add another method to the Action type that does that for you. For example, in Python:
class MyActionType: def __init__(self, dependency1, dependency2): self.dep1 = dependency1 self.dep2 = dependency2 def do_action(self, param1, param2): ... def prepare_action(self, param1, param2): def prepped_action(): return self.do_action(param1, param2) return prepped_action
Here, the Action type provides a
prepare_action method that will return a function that takes no arguments and will call the main Action method. That returned function can be added to the queue, and the queue can just call it when it comes up.
Despite all of this, I still think there’s a place for the Command pattern. It’s primary benefit is in a big, distributed system, I think. Plus, I don’t have any solid experience with either pattern, so you can definitely take my opinion with a grain of salt (though it IS pretty much the same pattern as suggested by some other smarter, more experienced people in a conference talk that I sadly can’t find right now). The Command pattern definitely does a better job of preventing spaghetti code, but it only takes a little extra discipline to stick with the Actions pattern properly.
So there you have it, my simpler replacement for the command pattern. I didn’t think I’d actually write this much if I didn’t explain the Command pattern. Now I’m REALLY glad I didn’t bother to describe it! Do with this pattern what you will; I just needed to get all of this off my chest.
I want to make something clear: My opinion of Commands and Command Handlers is not the same opinion of Events and Event Handlers. At a surface level, they’re almost exactly the same, but they do have one major difference that makes them far more worthwhile, and it’s not semantics. You can have multiple Handlers run for the same Event (or none!). And you can even have the same Handler registered for multiple Events! If you were to do the same with Commands, then you’re breaking the understanding of them.
A Command is “do this one thing”. Once you’ve done the thing, that thing can become an Event that can trigger other things to happen, but if there are multiple things that need to happen directly because of a Command, those are details that the Command abstracts over within a single Handler. And if certain things have to happen after others, the first thing can create an Event that the follower actions can subscribe to. And if your Event system runs multiple handlers in parallel, you could even potentially run multiple follower actions at the same time that all only depend on a previous action, which is an easier way to parallelize than to do it directly in the Command Handler.
Abstractly, Events don’t always have to have subscribers, either! Business logic may demand that certain handlers always be ready, but that’s specific to certain Events, not to the idea of Events. For example, a secondary system may subscribe when it is available, and after it’s satisfied, it can unsubscribe. If nothing is subscribed to a Command in order to carry it out, is it really a Command?
Plus, there’s that whole naming thing again. With Commands, you name one half “DoThisCommand” and the other half “DoThisHandler”. With my Action system, you have a single type or function just called “DoThis”. With Events, you have Events either named “ThisHappenedEvent” or even just “ThisHappened”, and the Event Handlers are named after what they do. Because there can be multiple, you don’t WANT to name them “ThisHappenedHandler”.
Carried out to their full potential, every function call could effectively be done via Commands, Events, and their Handlers. There is a point where you’re taking it too far, and I believe that most of the time the Command pattern isn’t that useful, but the Event one is.