Creating a command and query bus using Symfony Messenger
Symfony
CQRS
DDD

A simple command/query bus implementation using symfony/messenger
requires three basic interfaces: command, command handler, and command bus:
PHP
interface CommandInterface { } Â interface CommandHandlerInterface { } Â interface CommandBusInterface { /** * @throws Exception */ public function dispatch(CommandInterface $command): void; }
We then need to tell Symfony what to do with these interfaces, of course, especially with the bus one. This requires two things: updating the framework.messenger configuration, and then creating an actual implementation for the interface.
Yaml
messenger: buses: command_bus: ~
PHP
class SymfonyMessengerCommandBus implements CommandBusInterface { public function __construct(private MessageBusInterface $symfonyMessengerMessageBus) { } Â public function dispatch(CommandInterface $command): void { try { $this->symfonyMessengerMessageBus->dispatch($command); } catch (HandlerFailedException $exception) { throw $exception->getPrevious() ?? $exception; } } }
That's actually it! It is a quick and easy configuration. Now you'll be able to create your own commands which extend CommandInterface
, and Symfony messenger will forward them to the correct handler
As you may have guesses, the CommandHandlerInterface
isn't technically needed, and it contains no actual methods. This is because Symfony requires you to define __invoke()
method in order to process the command, and due to the Liskov Substitution Principle, you are not allowed to restrict a parameter type in an interface implementation, something which would happen here if we defined the __invoke(CommandInterface $command)
: void method in the handler interface.
It's a slight compromise due to how the Symfony library works, but having the handler interface allows us to clear mark those services which are supposed to be command handlers, and in our opinion this is a good tradeoff.