diff --git a/components/messenger.rst b/components/messenger.rst
index 2700e3eec55..06d618190a0 100644
--- a/components/messenger.rst
+++ b/components/messenger.rst
@@ -291,5 +291,14 @@ loop, the message bus will add a :class:`Symfony\\Component\\Messenger\\Asynchro
envelope item to the message envelopes and the :class:`Symfony\\Component\\Messenger\\Asynchronous\\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/messenger.rst b/messenger.rst
index 14f5f4d906e..70e8926114d 100644
--- a/messenger.rst
+++ b/messenger.rst
@@ -421,112 +421,6 @@ like this:
The first argument is the receiver's service name. It might have been created by
your ``transports`` configuration or it can be your own receiver.
-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
----------
@@ -961,4 +855,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