Some questions about the command bus
Matthias Noback
So far we’ve had three posts in this series about commands, events and their corresponding buses and handlers:
Now I’d like to take the time to answer some of the very interesting questions that by readers.
The difference between commands and events
Robert asked:
[…], could you possibly explain what are the main differences between a command bus and an even dispatcher?
Marc and Florian answered the question already, but I’d like to repeat the answer here. The main difference is: events tell you that something has happened, while a command tells you that something needs to happen. From this it follows that nobody might be interested in an event, but somebody should be interested in a command, because it has to be handled somehow.
Disadvantages of using a command bus
mTorres asked
[…] is it great that you state the advantages but can you also post the disadvantages?
Some suggestions are offered, which also recur in other people’s comments:
Increases complexity with another layer of abstraction […]
You must glue each command with its handler somewhere and load that information on every request […] like the DI graph
To respond to the first reason: in my experience the level of complexity drops when you start using commands and the command bus. As mentioned it helps you disentangle the technical details of the input from the domain-specific actions that will be executed in the core of your application. Indeed, this adds a lot of flexibility as well.
The second reason is not a problem either; the number of services in your DI container doesn’t affect performance at
all. What matters is the number of objects that you actually instantiate. This number is still very low, because the
SimpleBus
packages provide lazy-loading of command and event handlers out-of-the-box.
One disadvantage that I can come up with myself is that it may require a bit more thinking from the developer (this is not meant to be cynical by the way!). Using commands and events (and also separating command from query responsibilities) generally leads to better design because you think harder about the characteristics of your domain. Still, this takes a bit more effort, which you might not want to put in if the application is not supposed to live a long life (i.e. needs no or just a little bit of maintenance).
One other disadvantage may be that you will have more classes. To me this is more like an advantage, since if the application still does the same thing, this means that those extra classes are highly focused on performing one task well.
One, maybe bigger disadvantage, might be that it’s not as simple anymore to offer CRUD-style actions. Commands are task-oriented instead of data-oriented. Still, you should keep in mind that just like you don’t need go “DDD-all-the-way”, you don’t need to use commands everywhere in your application.
The command as constructor argument
Daniel S asked:
[…] why does the Command Handler not receive the actual command in the constructor?
class SignUpHandler implements CommandHandler { public function __construct(SignUpCommand $command) { /* ... */ } }
Florian answered this one quite nicely:
I really think it should be passed as an argument of the
handle()
method. Otherwise, one handler instance = one command. You wouldn’t be able to process 2 different command instances with the same handler instance.
That’s right. A handler itself is an immutable service, which probably needs other immutable services to do its job. Hence, the constructor should be reserved for injecting dependencies. Then the same handler can be used to handle multiple instances of the same type of command.
How to return a value from the command bus
Gabriel Birke asked:
How would the controller know that the signup was successful or not and display the correct message?
That’s a very good question. And again, Florian provided some great answers already. Several kinds of problems may occur if we handle a command in the same process:
- The user may have provided invalid input data
- The provided input data may result in an invalid state
- An unexpected runtime error may occur (e.g. network failure)
We can already catch validation errors before we hand the command over to the command bus. In fact, we should verify that the command itself contains valid data and provide human-oriented error messages to the user if it doesn’t. The other two kind of problems will result in regular failures, just like they would in a non-command oriented application. They can be handled in any way you like (e.g. allowing the user to retry the action, or show an error page).
Any other execution path should be considered the happy path: everything goes well. So if the command bus finally returns control to the controller that asked it to handle a command, you can assume that no problems occurred and, for instance, redirect to a “thank you” page.
Something that Florian already mentioned as well: when using commands you follow the Command-query separation (CQS) principle: a function is either a query (i.e. it returns something) or a command (i.e. it affects state). Both are mutually exclusive. So a command is not supposed to return anything and a query is not supposed to modify anything.
Could commands handle themselves?
mTorres asked another question which is highly relevant:
[…] maybe we could create Commands that executes themselves (with the
__invoke
method)? It will be less flexible but you’ll have also less complexity to handle, what do you think?
It is an intriguing idea. However, you have to keep in mind that the set of commands that might be able to handle themselves is very small. State machines and situations where command objects are created ad hoc might allow for self-handling commands. See also an example of self-executing commands from Ross Tuck’s library Tactician, which has more or less the same goals as SimpleBus.
The notion of self-executing commands recently resulted in an interesting debate when Taylor Otwell demoed the use of self-executing commands in the new version of Laravel. As a follow-up, Ross wrote something which I consider a highly convincing recommendation against the use of these types of commands.
The takeaway of this is that:
- Commands - as discussed in this series - should be simple messages only.
- Related behavior should always be separated from the message itself.
Of course, SimpleBus
(as well as Tactician
) allows you to implement commands in any way, so you could easily
replicate Laravel’s self-handling commands. In fact, let me just show you a rough outline:
use SimpleBus\Command\Command;
use SimpleBus\Command\Bus\CommandBus;
use SimpleBus\Command\Bus\RemembersNext;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Bus\SelfHandling;
interface HandlesItself extends Command
{
}
class SupportsSelfHandlingCommands implements CommandBus
{
use RemembersNext;
private $container;
public function __construct(Container $container)
{
$this->container = $container;
}
public function handle(Command $command)
{
if ($command instanceof HandlesItself) {
$arguments = $this->resolveArguments(;
$this->container->call([$command, 'handle']);
/*
* We consider the command handled,
* so we don't call the next command bus
*/
} else {
$this->next();
}
}
}
You only need to make sure that this particular command bus is called before any regular command handler might be called.
Conclusion
Thanks everyone for asking these questions. As you can see, there are quite a lot of details and reasoning behind the answers.
The next post will be about collecting events.