Building a PHP Command Bus

Lately I’ve been looking for new ways on how Domain Logic could interact with the Application layer. And even if we’re not formerly using DDD it’s something we have to do, maybe using a different terminology. But we still need to do it. We have to strictly define the thin boundary where the outside world (controller and framework) integrates with your core business logic.

With the rise of MVC pattern, usually business logic where going into the Controller (The Fat Controller). Then we realized things were not structured well and like Symfony enforces, we started using the “thin controller, fat model” approach. But still it’s not well defined this little boundary between domain business logic and framework layer.

In many Symfony projects I see defined Application services as middleware to the Domain layer. The problem with this approach sometimes, is that we don’t get the full picture on how our application’s state is being changed or simply on how our application works. Specially when application services’ number grows constantly. Is in this moment where the Command Bus approach is coming to help us.

Why Command Bus

The Command Bus pattern is trying to decouple as much as it can the controller, the framework itself to the domain layer, which should be totally unaware of application’s upper layer. It keeps the user interface logic separated from your models. We can go and look at our commands and we’re immediately capable to see what our system is able to do. It Enforces discoverability for your application, even without opening a source file. It’s mostly used for writing to our domain layer.

Command

One important component of Command Bus is the Command. It’s a strictly defined message, it’s immutable.The Command is not more than a Data Transfer Object which can be used by the Command Handler. It represents the outside request structured in a well formalized way.

<?php

namespace
Buxus\Command;

interface Command { }
No, it’s not this kind of Command (https://xkcd.com/898/)

Command Handler

The Command Handler component is the place where all the magic happens. It’s orchestrating the logic to process the Command and it’s not interacting in any ways with the user interface. It’s here where our request is being dispatched and handled, where the things happen.

<?php

namespace
Buxus\Handler;

use Buxus\Command\Command;

interface CommandHandler
{
/**
*
@param Command $command
*/
public function handle(Command $command);
}

Command Bus

The king is the Command Bus. It usually has only one method which is used to dispatch a given Command into the Bus and it’s simple to decorate. It’s like a router that maps a Command to a Command Handler. This is a really important concept.

The relation between Command and Command Handler is 1:1. A Command has only one Command Handler and vice versa.
<?php

namespace
Buxus\Bus;

use Buxus\Command\Command;

interface CommandBus
{
/**
*
@param Command $command
*/
public function dispatch(Command $command);
}

As we said in order to make it work, we need a data structure where we can store the relationships between Commands and Command Handlers. For each Command we need a specific Command Handler. We’re going to use a simple immutable collection

<?php

namespace
Buxus\Map;

use Buxus\Handler\CommandHandler;

interface CommandHandlerMap
{
/**
*
@param string $command
*
@return CommandHandler
*/
public function getCommand($command);
}

We have the Command, Command Handler, Command Bus and the CommandHandlerMap. We need a component that given a command retrieves the corresponding Command Handler from the Map. The Command Handler Locator

<?php

namespace
Buxus\Handler;

use Buxus\Command\Command;

interface CommandHandlerLocator
{
/**
*
@param Command $command
*
@return CommandHandler
*/
public function getHandler(Command $command);
}

Once we’ve implemented the Command Bus and the Command Handler Map we end up with something like this

<?php
$commandHandlerMap = [
// .. many other commands
CreateProductCommand::class => new CreateProductHandler($pdo)
];

$standardCommandBus = new \Buxus\Bus\StandardCommandBus(
new \Buxus\Handler\CommandHandlerLocator(
new \Buxus\Map\InMemoryCommandHandlerMap($commandHandlerMap)
)
);
$standardCommandBus->dispatch(new CreateProductCommand('beer'));

For more examples check out the repo https://github.com/0x13a/buxus

Conclusions

This is a basic example, just to understand how does the Command Bus work. It can clearly be improved. For instance we could avoid to instantiate the Command Handlers directly into the Command Handler Map and only specify the class name, letting then the Command Bus instantiating and resolving the Command Handler dependencies automatically.


Hope you understood the concept, for more production ready & extended command buses you can also take a look at Tactician or SimpleBus libraries.

Thanks for reading!