You can define your message handlers like every service in Symfony. But you need to tell your message bus that a handler is a target for a message object.
There are two ways to route a message object to a handler. You can configure it using a tag at the handler definition or directly at the bus configuration.
The basis for both ways is the service definition of the handler
# app/config/services.yml
services:
Acme\Command\RegisterUserHandler: ~
and a configured message bus:
# app/config/config.yml or (flex) config/packages/prooph_service_bus.yaml
prooph_service_bus:
command_buses:
acme_command_bus: ~
The command bus is used as example, handlers are routed to each bus (nearly) the same way.
If you don't know about tags in Symfony please have a look at the official documentation.
To route a message to a specific handler, we just need to add a tag to its service definition:
# app/config/services.yml
services:
Acme\Command\RegisterUserHandler:
tags:
- { name: 'prooph_service_bus.acme_command_bus.route_target', message: Acme\Command\RegisterUser }
The name of the tag is simple prooph_service_bus.<name-of-the-bus>.route_target
.
The additional message
attribute defines the message name (message class by default) that is routed to the handler.
If your handler handles multiple messages, you need to add a tag for each one.
If you are not afraid of a little bit magic
you can use automatic message detection to simplify the configuration and make it less vulnerable for refactoring.
Instead of defining the message
attribute add a message_detection
attribute:
# app/config/services.yml
services:
Acme\Command\RegisterUserHandler:
tags:
- { name: 'prooph_service_bus.acme_command_bus.route_target', message_detection: true }
The bundle will try to detect the message itself by analyzing the methods of the handler and creating instances of the message objects. But don't worry about performance because this will happen on compiling.
Important: Automatic message detection can only detect handlers where either the message implements
Prooph\Common\Messaging\HasMessageName
or the name of the method is__invoke
. In both cases the object must be a non abstract class.
Hint: If you rely on automatic message detection and your handler handles multiple messages of the same message bus, you need to tag the handler just once.
Hint: Registering handlers can be extremely compact using the new DI features of Symfony 3.3:
services: _defaults: autowire: true App\Command\: resource: '../../src/Command/*Handler.php' tags: [{ name: 'prooph_service_bus.acme_command_bus.route_target', message_detection: true }]
If you are no fan of service tags, you can route the messages directly at the bus configuration:
# app/config/config.yml or (flex) config/packages/prooph_service_bus.yaml
prooph_service_bus:
command_buses:
acme_command_bus:
router:
routes:
'Acme\Command\RegisterUser': 'acme.command.register_user_handler'
This will work the same way for query buses.
When configuring event buses you can pass an array of service IDs for each event instead of a single service ID. This is necessary because events can be routed to multiple event handlers.
Hint: To get autocompletion in some IDEs you can prepend the service id with an
@
("@acme.command.register_user_handler"
).The bundle will recognize this and find your handler anyway.
Which way you choose to configure your routing is up to you. Each way has its benefits and drawbacks.
The async switch configuration allows to handle some messages asynchronously.
For a general introduction into the AsyncSwitchMessageRouter
please have a look at the official documentation.
In short: The AsyncSwitchMessageRouter
decorates the configured router and sends messages that
implement Prooph\ServiceBus\Async\AsyncMessage
to a special message producer.
Other messages will be handled by the decorated router.
To use this switch with symfony, configure your own message producer (it must implement Prooph\ServiceBus\Async\MessageProducer
)
and pass its service id to the configuration:
# app/config/config.yml or (flex) config/packages/prooph_service_bus.yaml
prooph_service_bus:
command_buses:
acme_command_bus:
router:
async_switch: 'my_async_message_producer'