diff --git a/components/messenger.rst b/components/messenger.rst index bc418a6b3d7..fe529287ae2 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -304,5 +304,14 @@ loop, the message bus will add a :class:`Symfony\\Component\\Messenger\\Stamp\\R stamp to the message envelopes and the :class:`Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware` middleware will know it should not route these messages again to a transport. +Learn more +---------- +.. toctree:: + :maxdepth: 1 + :glob: + + /messenger + /messenger/* + .. _blog posts about command buses: https://matthiasnoback.nl/tags/command%20bus/ .. _SimpleBus project: http://simplebus.io diff --git a/console/coloring.rst b/console/coloring.rst index 1bc0168d6d4..e2b9d3d5cbd 100644 --- a/console/coloring.rst +++ b/console/coloring.rst @@ -9,9 +9,9 @@ output (e.g. important messages, titles, comments, etc.). By default, the Windows command console doesn't support output coloring. The Console component disables output coloring for Windows systems, but if your commands invoke other scripts which emit color sequences, they will be - wrongly displayed as raw escape characters. Install the `Cmder`_, `ConEmu`_, `ANSICON`_ - ,`Mintty`_ (used by default in GitBash and Cygwin) or `Hyper`_ free applications - to add coloring support to your Windows command console. + wrongly displayed as raw escape characters. Install the `Cmder`_, `ConEmu`_, + `ANSICON`_, `Mintty`_ (used by default in GitBash and Cygwin) or `Hyper`_ + free applications to add coloring support to your Windows command console. Using Color Styles ------------------ diff --git a/messenger.rst b/messenger.rst index a6d17644d1a..65b370815b6 100644 --- a/messenger.rst +++ b/messenger.rst @@ -423,112 +423,6 @@ your ``transports`` configuration or it can be your own receiver. It also requires a ``--bus`` option in case you have multiple buses configured, which is the name of the bus to which received messages should be dispatched. -Multiple Buses --------------- - -If you are interested in architectures like CQRS, you might want to have multiple -buses within your application. - -You can create multiple buses (in this example, a command bus and an event bus) like -this: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/messenger.yaml - framework: - messenger: - # The bus that is going to be injected when injecting MessageBusInterface: - default_bus: messenger.bus.commands - - # Create buses - buses: - messenger.bus.commands: ~ - messenger.bus.events: ~ - - .. code-block:: xml - - - - - - - - - - - - - - .. code-block:: php - - // config/packages/messenger.php - $container->loadFromExtension('framework', array( - 'messenger' => array( - 'default_bus' => 'messenger.bus.commands', - 'buses' => array( - 'messenger.bus.commands' => null, - 'messenger.bus.events' => null, - ), - ), - )); - -This will generate the ``messenger.bus.commands`` and ``messenger.bus.events`` services -that you can inject in your services. - -.. note:: - - To register a handler only for a specific bus, add a ``bus`` attribute to - the handler's service tag (``messenger.message_handler``) and use the bus - name as its value. - -Type-hints and Auto-wiring -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Auto-wiring is a great feature that allows you to reduce the amount of configuration -required for your service container to be created. When using multiple buses, by default, -the auto-wiring will not work as it won't know which bus to inject in your own services. - -In order to clarify this, you can use the DependencyInjection's binding capabilities -to clarify which bus will be injected based on the argument's name: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - services: - _defaults: - # ... - - bind: - $commandBus: '@messenger.bus.commands' - $eventBus: '@messenger.bus.events' - - .. code-block:: xml - - - - - - - - - - - - - Middleware ---------- @@ -928,4 +822,12 @@ will give you access to the following services: #. ``messenger.sender.yours``: the sender; #. ``messenger.receiver.yours``: the receiver. +Learn more +---------- +.. toctree:: + :maxdepth: 1 + :glob: + + /messenger/* + .. _`enqueue's transport`: https://github.com/php-enqueue/messenger-adapter diff --git a/messenger/multiple_buses.rst b/messenger/multiple_buses.rst new file mode 100644 index 00000000000..e3b221450d6 --- /dev/null +++ b/messenger/multiple_buses.rst @@ -0,0 +1,292 @@ +.. index:: + single: Messenger; Multiple buses + +Multiple Buses +============== + +A common architecture when building applications is to separate commands from +queries. Commands are actions that do something and queries fetch data. This +is called CQRS (Command Query Responsibility Segregation). See Martin Fowler's +`article about CQRS`_ to learn more. This architecture could be used together +with the Messenger component by defining multiple buses. + +A **command bus** is a little different from a **query bus**. For example, command +buses usually don't provide any results and query buses are rarely asynchronous. +You can configure these buses and their rules by using middleware. + +It might also be a good idea to separate actions from reactions by introducing +an **event bus**. The event bus could have zero or more subscribers. + +.. configuration-block:: + + .. code-block:: yaml + + framework: + messenger: + # The bus that is going to be injected when injecting MessageBusInterface + default_bus: messenger.bus.commands + buses: + messenger.bus.commands: + middleware: + - validation + - doctrine_transaction + messenger.bus.queries: + middleware: + - validation + messenger.bus.events: + middleware: + - allow_no_handler + - validation + + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: php + + // config/packages/messenger.php + $container->loadFromExtension('framework', array( + 'messenger' => array( + // The bus that is going to be injected when injecting MessageBusInterface + 'default_bus' => 'messenger.bus.commands', + 'buses' => array( + 'messenger.bus.commands' => array( + 'middleware' => array( + 'validation', + 'doctrine_transaction', + ), + ), + 'messenger.bus.queries' => array( + 'middleware' => array( + 'validation', + ), + ), + 'messenger.bus.events' => array( + 'middleware' => array( + 'validation', + 'allow_no_handler', + ), + ), + ), + ), + )); + +This will generate the ``messenger.bus.commands``, ``messenger.bus.queries`` +and ``messenger.bus.events`` services that you can inject in your services. + +Type-hints and Auto-wiring +-------------------------- + +Auto-wiring is a great feature that allows you to reduce the amount of configuration +required for your service container to be created. By using ``MessageBusInterface`` +as argument typehint in your services, the default configured bus will be injected +(i.e ``messenger.bus.commands`` in above examples). + +When working with multiple buses, you can use the ``DependencyInjection`` component's +binding capabilities to clarify which bus will be injected based on the argument's name: + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + _defaults: + # ... + + bind: + $commandBus: '@messenger.bus.commands' + $queryBus: '@messenger.bus.queries' + $eventBus: '@messenger.bus.events' + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // config/services.php + + $container->bind('$commandBus', 'messenger.bus.commands'); + $container->bind('$queryBus', 'messenger.bus.queries'); + $container->bind('$eventBus', 'messenger.bus.events'); + +Restrict handlers per bus +------------------------- + +By default, each handler will be available to handle messages on *all* +of your buses. To prevent dispatching a message to the wrong bus without an error, +you can restrict each handler to a specific bus using the `messenger.message_handler` tag: + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + App\MessageHandler\SomeCommandHandler: + tags: [{ name: messenger.message_handler, bus: messenger.bus.commands }] + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // config/services.php + + $container->services() + ->set(App\MessageHandler\SomeCommandHandler::class) + ->tag('messenger.message_handler', ['bus' => 'messenger.bus.commands']); + +This way, the ``App\MessageHandler\SomeCommandHandler`` handler will only be +known by the ``messenger.bus.commands`` bus. + +You can also automatically add this tag to a number of classes by following +a naming convention and registering all of the handler services by name with +the correct tag: + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + + # put this after the `App\` line that registers all your services + command_handlers: + namespace: App\MessageHandler\ + resource: '%kernel.project_dir%/src/MessageHandler/*CommandHandler.php' + tags: + - { name: messenger.message_handler, bus: messenger.bus.commands } + + query_handlers: + namespace: App\MessageHandler\ + resource: '%kernel.project_dir%/src/MessageHandler/*QueryHandler.php' + tags: + - { name: messenger.message_handler, bus: messenger.bus.queries } + + .. code-block:: xml + + + + + + + + + + + + + + + + + + .. code-block:: php + + // config/services.php + + // Command handlers + $container->services() + ->load('App\MessageHandler\\', '%kernel.project_dir%/src/MessageHandler/*CommandHandler.php') + ->tag('messenger.message_handler', ['bus' => 'messenger.bus.commands']); + + // Query handlers + $container->services() + ->load('App\MessageHandler\\', '%kernel.project_dir%/src/MessageHandler/*QueryHandler.php') + ->tag('messenger.message_handler', ['bus' => 'messenger.bus.queries']); + +Debugging the buses +------------------- + +The ``debug:messenger`` command lists available messages & handlers per bus. +You can also restrict the list to a specific bus by providing its name as argument. + +.. code-block:: terminal + + $ bin/console debug:messenger + + Messenger + ========= + + messenger.bus.commands + ---------------------- + + The following messages can be dispatched: + + --------------------------------------------------------------------------------------- + App\Message\DummyCommand + handled by App\MessageHandler\DummyCommandHandler + App\Message\MultipleBusesMessage + handled by App\MessageHandler\MultipleBusesMessageHandler + --------------------------------------------------------------------------------------- + + messenger.bus.queries + --------------------- + + The following messages can be dispatched: + + --------------------------------------------------------------------------------------- + App\Message\DummyQuery + handled by App\MessageHandler\DummyQueryHandler + App\Message\MultipleBusesMessage + handled by App\MessageHandler\MultipleBusesMessageHandler + --------------------------------------------------------------------------------------- + +.. _article about CQRS: https://martinfowler.com/bliki/CQRS.html