Using the building blocks supplied by the SimpleBus/MessageBus
library you can create a command bus, which is basically a message bus, with some middlewares and a map of message handlers. This is described in the documentation of CommandBus <Guides/command_bus>
.
This bundle provides the command_bus
service which is an instance of SimpleBus\SymfonyBridge\Bus\CommandBus
. Wherever you like, you can let it handle commands, e.g. inside a container-aware controller:
// $command is an arbitrary object that will be passed to the command handler
$command = ...;
$this->get('command_bus')->handle($command);
However, you are encouraged to properly inject the command_bus
service as a dependency whenever you need it:
services:
some_service:
class: Acme\Foobar
arguments:
- "@command_bus"
This bundle can be used with Symfony's Autowiring out of the box.
Simply inject SimpleBus\SymfonyBridge\Bus\CommandBus
in your controller or service:
namespace App\Controller;
use SimpleBus\SymfonyBridge\Bus\CommandBus;
class UpdatePhoneNumberController
{
private $commandBus;
public function __construct(CommandBus $commandBus)
{
$this->commandBus = $commandBus;
}
public function __invoke(Request $request)
{
$this->commandBus->handle(new SavePhoneNumberCommand($request->get('phone')));
}
}
As described in the MessageBus documentation <Guides/command_bus>
you can delegate the handling of particular commands to command handlers. This bundle allows you to register your own command handlers by adding the command_handler
tag to the command handler's service definition:
services:
register_user_command_handler:
class: Fully\Qualified\Class\Name\Of\RegisterUserCommandHandler
tags:
- { name: command_handler, handles: Fully\Qualified\Class\Name\Of\RegisterUser }
Note
Command handlers are lazy-loaded
Since only one of the command handlers is going to handle any particular command, command handlers are lazy-loaded. This means that their services should be defined as public services (i.e. you can't use public: false
for them).
Any service that is a PHP callable itself can be used as a command handler. If a service itself is not callable, SimpleBus looks for a __invoke
or handle
method and calls it. If you want to use a custom method, just add a method
attribute to the command_handler
tag:
services:
register_user_command_handler:
...
tags:
- { name: command_handler, handles: ..., method: registerUser }
To find the correct command handler for a given command, the name of the command is used. This can be either 1) its fully-qualified class name (FQCN) or, 2) if the command implements the SimpleBus\Message\Name\NamedMessage
interface, the value returned by its static messageName()
method. By default, the first strategy is used, but you can configure it in your application configuration:
# app/config/config.yml
command_bus:
# default value for this key is "class_based"
command_name_resolver_strategy: named_message
When you change the strategy, you also have to change the value of the handles
attribute of your command handler service definitions:
services:
register_user_command_handler:
class: Fully\Qualified\Class\Name\Of\RegisterUserCommandHandler
tags:
- { name: command_handler, handles: register_user }
Make sure that the value of handles
matches the return value of RegisterUser::messageName()
.
As described in the MessageBus documentation you can extend the behavior of the command bus by adding middleware to it. This bundle allows you to register your own middleware by adding the command_bus_middleware
tag to the middleware service definition:
services:
specialized_command_bus_middleware:
class: YourSpecializedCommandBusMiddleware
public: false
tags:
- { name: command_bus_middleware, priority: 100 }
By providing a value for the priority
tag attribute you can influence the order in which middlewares are added to the command bus.
Note
Middlewares are not lazy-loaded
Whenever you use the command bus, you also use all of its middlewares, so command bus middlewares are not lazy-loaded. This means that their services should be defined as private services (i.e. you should use public: false
). See also: Marking Services as public / private
If you want to log every command that is being handled, enable logging in config.yml
:
# app/config/config.yml
command_bus:
middlewares:
logger: true
Messages will be logged to the command_bus
channel with %simple_bus.command_bus.logging.level%
(defaults to debug
) level.
By default, calls to $commandBus->handle($command)
will not be executed sequentially. Instead, the $command
will be pushed to a in-memory queue in the SimpleBus\Message\Bus\Middleware\FinishesHandlingMessageBeforeHandlingNext
middleware. Once the handler that triggered the command is finished, the in-memory queue will be processed.
If you don't like this behaviour you can disable it in config.yml
:
# app/config/config.yml
command_bus:
middlewares:
finishes_command_before_handling_next: false