CommandBusBundle¶
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
.
Using the 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:
1 2 3 4 | // $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:
1 2 3 4 5 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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')));
}
}
|
Registering command handlers¶
As described in the MessageBus documentation
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:
1 2 3 4 5 | 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).
Command handlers are callables¶
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:
1 2 3 4 5 | services:
register_user_command_handler:
...
tags:
- { name: command_handler, handles: ..., method: registerUser }
|
Setting the command name resolving strategy¶
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:
1 2 3 4 | # 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:
1 2 3 4 5 | 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()
.
Adding command bus middleware¶
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:
1 2 3 4 5 6 | 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
Logging¶
If you want to log every command that is being handled, enable logging
in config.yml
:
1 2 3 4 | # 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.
Nested commands execution¶
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
:
1 2 3 4 | # app/config/config.yml
command_bus:
middlewares:
finishes_command_before_handling_next: false
|