From a4bc592400bbd58f54c1e4eaf1b7f66303da63e1 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Thu, 3 May 2018 14:23:52 +0100 Subject: [PATCH 01/11] Rename the adapters to transport --- components/messenger.rst | 10 +++---- messenger.rst | 60 ++++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/components/messenger.rst b/components/messenger.rst index 53e4687812e..a784cce16f7 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -86,11 +86,11 @@ that will do the required processing for your message:: } } -Adapters --------- +Transports +---------- -In order to send and receive messages, you will have to configure an adapter. An -adapter will be responsible of communicating with your message broker or 3rd parties. +In order to send and receive messages, you will have to configure a transport. An +transport will be responsible of communicating with your message broker or 3rd parties. Your own sender ~~~~~~~~~~~~~~~ @@ -190,4 +190,4 @@ To allow us to receive and send messages on the same bus and prevent an infinite loop, the message bus is equipped with the ``WrapIntoReceivedMessage`` middleware. It will wrap the received messages into ``ReceivedMessage`` objects and the ``SendMessageMiddleware`` middleware will know it should not route these -messages again to an adapter. +messages again to a transport. diff --git a/messenger.rst b/messenger.rst index 94cfb9a63b3..3212a71503b 100644 --- a/messenger.rst +++ b/messenger.rst @@ -5,7 +5,7 @@ How to Use the Messenger ======================== Symfony's Messenger provide a message bus and some routing capabilities to send -messages within your application and through adapters such as message queues. +messages within your application and through transports such as message queues. Before using it, read the :doc:`Messenger component docs ` to get familiar with its concepts. @@ -70,19 +70,19 @@ Once you've created your handler, you need to register it: If the message cannot be guessed from the handler's type-hint, use the ``handles`` attribute on the tag. -Adapters --------- +Transports +---------- The communication with queuing system or third parties is delegated to -libraries for now. The built-in AMQP adapter allows you to communicate with +libraries for now. The built-in AMQP transport allows you to communicate with most of the AMQP brokers such as RabbitMQ. .. note:: - If you need more message brokers, you should have a look to `Enqueue's adapter`_ + If you need more message brokers, you should have a look to `Enqueue's transport`_ which supports things like Kafka, Amazon SQS or Google Pub/Sub. -An adapter is registered using a "DSN", which is a string that represents the +A transport is registered using a "DSN", which is a string that represents the connection credentials and configuration. By default, when you've installed the messenger component, the following configuration should have been created: @@ -91,7 +91,7 @@ the messenger component, the following configuration should have been created: # config/packages/messenger.yaml framework: messenger: - adapters: + transports: amqp: "%env(MESSENGER_DSN)%" .. code-block:: bash @@ -111,7 +111,7 @@ Routing ------- Instead of calling a handler, you have the option to route your message(s) to a -sender. Part of an adapter, it is responsible for sending your message somewhere. +sender. Part of a transport, it is responsible for sending your message somewhere. You can configure which message is routed to which sender with the following configuration: @@ -120,7 +120,7 @@ configuration: framework: messenger: routing: - 'My\Message\Message': amqp # The name of the defined adapter + 'My\Message\Message': amqp # The name of the defined transport Such configuration would only route the ``My\Message\Message`` message to be asynchronous, the rest of the messages would still be directly handled. @@ -132,7 +132,7 @@ You can route all classes of message to a sender using an asterisk instead of a framework: messenger: routing: - 'My\Message\MessageAboutDoingOperationalWork': another_adapter + 'My\Message\MessageAboutDoingOperationalWork': another_transport '*': amqp A class of message can also be routed to multiple senders by specifying a list: @@ -166,25 +166,25 @@ like this: $ bin/console messenger:consume-messages amqp The first argument is the receiver's service name. It might have been created by -your ``adapters`` configuration or it can be your own receiver. +your ``transports`` configuration or it can be your own receiver. -Your own Adapters ------------------ +Your own Transport +------------------ -Once you have written your adapter's sender and receiver, you can register your -adapter factory to be able to use it via a DSN in the Symfony application. +Once you have written your transport's sender and receiver, you can register your +transport factory to be able to use it via a DSN in the Symfony application. -Create your adapter Factory +Create your Transport Factory ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You need to give FrameworkBundle the opportunity to create your adapter from a -DSN. You will need an adapter factory:: +You need to give FrameworkBundle the opportunity to create your transport from a +DSN. You will need an transport factory:: - use Symfony\Component\Messenger\Adapter\Factory\AdapterFactoryInterface; + use Symfony\Component\Messenger\Transport\Factory\AdapterFactoryInterface; use Symfony\Component\Messenger\Transport\ReceiverInterface; use Symfony\Component\Messenger\Transport\SenderInterface; - class YourAdapterFactory implements AdapterFactoryInterface + class YourTransportFactory implements TransportFactoryInterface { public function createReceiver(string $dsn, array $options): ReceiverInterface { @@ -198,7 +198,7 @@ DSN. You will need an adapter factory:: public function supports(string $dsn, array $options): bool { - return 0 === strpos($dsn, 'my-adapter://'); + return 0 === strpos($dsn, 'my-transport://'); } } @@ -207,22 +207,22 @@ Register your factory .. code-block:: xml - - + + -Use your adapter -~~~~~~~~~~~~~~~~ +Use your transport +~~~~~~~~~~~~~~~~~~ -Within the ``framework.messenger.adapters.*`` configuration, create your -named adapter using your own DSN: +Within the ``framework.messenger.transports.*`` configuration, create your +named transport using your own DSN: .. code-block:: yaml framework: messenger: - adapters: - yours: 'my-adapter://...' + transports: + yours: 'my-transport://...' In addition of being able to route your messages to the ``yours`` sender, this will give you access to the following services: @@ -230,4 +230,4 @@ will give you access to the following services: #. ``messenger.sender.yours``: the sender. #. ``messenger.receiver.yours``: the receiver. -.. _`enqueue's adapter`: https://github.com/sroze/enqueue-bridge +.. _`enqueue's transport`: https://github.com/sroze/enqueue-bridge From 3ff8cfe2bbbb665d1b61773b699b025f103f80cd Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Thu, 3 May 2018 14:34:03 +0100 Subject: [PATCH 02/11] Add multiple buses configuration and type-hint example --- messenger.rst | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/messenger.rst b/messenger.rst index 3212a71503b..197afb017f8 100644 --- a/messenger.rst +++ b/messenger.rst @@ -168,6 +168,57 @@ 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 into architectures like CQRS, you might want to have multiple +buses within your application. + +You can create multiple buses (in this example, a command and an event bus) like +this: + +.. code-block:: yaml + + framework: + messenger: + # The bus that is going to be injected when injecting MessageBusInterface: + default_bus: commands + + # Create buses + buses: + commands: ~ + events: ~ + +This will generate the ``messenger.bus.commands`` 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. When using multiple buses, by default, +the auto-wiring will not work as it won't know why bus to inject in your own services. + +In order to clarify this, you will have to create your own decorators for your message +buses. Let's create one for your ``CommandBus``:: + + namespace App; + + use Symfony\Component\Messenger\AbstractMessageBusDecorator; + + final class CommandBus extends AbstractMessageBusDecorator + { + } + +Last step is to register your service (and explicit its argument) to be able to typehint +your ``CommandBus`` in your services: + +.. code-block:: yaml + + # config/services.yaml + services: + App\CommandBus: ['@messenger.bus.commands'] + Your own Transport ------------------ From ef70bc058af0966d30e9d430a0b2ef213409501a Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Thu, 3 May 2018 14:45:41 +0100 Subject: [PATCH 03/11] Add a documentation about the middlewares --- messenger.rst | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/messenger.rst b/messenger.rst index 197afb017f8..fda9f869b6e 100644 --- a/messenger.rst +++ b/messenger.rst @@ -219,6 +219,59 @@ your ``CommandBus`` in your services: services: App\CommandBus: ['@messenger.bus.commands'] +Middlewares +----------- + +What happens when you dispatch a message to a message bus(es) depends on its +middlewares (and their order). By default, the middlewares configured for each +bus looks like this. + +1. ``logging`` middleware. Responsible of logging the beginning and the end of the + message within the bus. + +2. _Your own middlewares__ + +3. ``route_messages`` middleware. Will route the messages your configured to their + corresponding sender and stop the middleware chain. + +4. ``call_message_handler`` middleware. Will call the message handler(s) for the + given message. + +Adding your own middlewares +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As described in the component documentation, you can add your own middlewares +within the buses to add some extra capabilities like this: + +.. code-block:: yaml + + framework: + messenger: + buses: + default: + middlewares: + # Works with the FQCN if the class discovery is enabled + - App\\Middleware\\MyMiddleware + + # Or with some service name + - app.middleware.yours + +Note that if the service is abstract, then a child service will be created per bus. + +Disabling default middlewares +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you don't want the default middlewares to be present on your bus, you can disable +them like this: + +.. code-block:: yaml + + framework: + messenger: + buses: + default: + default_middlewares: false + Your own Transport ------------------ From 10f46eb0626613fff3b7a3ea629d1db72db94b46 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 8 May 2018 21:04:52 +0100 Subject: [PATCH 04/11] Introduce the `TransportInterface` --- messenger.rst | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/messenger.rst b/messenger.rst index fda9f869b6e..8c00e08b9d8 100644 --- a/messenger.rst +++ b/messenger.rst @@ -279,30 +279,48 @@ Once you have written your transport's sender and receiver, you can register you transport factory to be able to use it via a DSN in the Symfony application. Create your Transport Factory -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You need to give FrameworkBundle the opportunity to create your transport from a DSN. You will need an transport factory:: - use Symfony\Component\Messenger\Transport\Factory\AdapterFactoryInterface; + use Symfony\Component\Messenger\Transport\TransportFactoryInterface; + use Symfony\Component\Messenger\Transport\TransportInterface; use Symfony\Component\Messenger\Transport\ReceiverInterface; use Symfony\Component\Messenger\Transport\SenderInterface; class YourTransportFactory implements TransportFactoryInterface { - public function createReceiver(string $dsn, array $options): ReceiverInterface + public function createTransport(string $dsn, array $options): TransportInterface { - return new YourReceiver(/* ... */); + return new YourTransport(/* ... */); } - public function createSender(string $dsn, array $options): SenderInterface + public function supports(string $dsn, array $options): bool { - return new YourSender(/* ... */); + return 0 === strpos($dsn, 'my-transport://'); } + } - public function supports(string $dsn, array $options): bool +The transport object is needs to implements the ``TransportInterface`` (which simply combine +the ``SenderInterface`` and ``ReceiverInterface``). It will look +like this:: + + class YourTransport implements TransportInterface + { + public function send($message) : void { - return 0 === strpos($dsn, 'my-transport://'); + // ... + } + + public function receive(callable $handler) : void + { + // ... + } + + public function stop() : void + { + // ... } } From a20286dc82e883aeafed65810f7fdf299a7f065d Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 8 May 2018 21:06:55 +0100 Subject: [PATCH 05/11] Add a note about the symfony serializer pack --- messenger.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/messenger.rst b/messenger.rst index 8c00e08b9d8..ce642d554e8 100644 --- a/messenger.rst +++ b/messenger.rst @@ -107,6 +107,15 @@ configure the following services for you: 1. A ``messenger.sender.amqp`` sender to be used when routing messages. 2. A ``messenger.receiver.amqp`` receiver to be used when consuming messages. +.. note:: + + In order to use Symfony's built-in AMQP adapter, you will need the Serializer + Component. Ensure that it is installed with: + + .. code-block:: terminal + + $ composer require symfony/serializer-pack + Routing ------- From 2409798251eb3b8bc2d38159aac0efd05f59766e Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 8 May 2018 21:08:20 +0100 Subject: [PATCH 06/11] Middleware does not have a plural --- messenger.rst | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/messenger.rst b/messenger.rst index ce642d554e8..3cf0233105e 100644 --- a/messenger.rst +++ b/messenger.rst @@ -228,17 +228,17 @@ your ``CommandBus`` in your services: services: App\CommandBus: ['@messenger.bus.commands'] -Middlewares ------------ +Middleware +---------- What happens when you dispatch a message to a message bus(es) depends on its -middlewares (and their order). By default, the middlewares configured for each -bus looks like this. +collection of middleware (and their order). By default, the middleware configured +for each bus looks like this: 1. ``logging`` middleware. Responsible of logging the beginning and the end of the message within the bus. -2. _Your own middlewares__ +2. _Your own collection of middleware__ 3. ``route_messages`` middleware. Will route the messages your configured to their corresponding sender and stop the middleware chain. @@ -246,10 +246,10 @@ bus looks like this. 4. ``call_message_handler`` middleware. Will call the message handler(s) for the given message. -Adding your own middlewares -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Adding your own middleware +~~~~~~~~~~~~~~~~~~~~~~~~~~ -As described in the component documentation, you can add your own middlewares +As described in the component documentation, you can add your own middleware within the buses to add some extra capabilities like this: .. code-block:: yaml @@ -258,7 +258,7 @@ within the buses to add some extra capabilities like this: messenger: buses: default: - middlewares: + middleware: # Works with the FQCN if the class discovery is enabled - App\\Middleware\\MyMiddleware @@ -267,11 +267,11 @@ within the buses to add some extra capabilities like this: Note that if the service is abstract, then a child service will be created per bus. -Disabling default middlewares -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Disabling default middleware +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you don't want the default middlewares to be present on your bus, you can disable -them like this: +If you don't want the default collection of middleware to be present on your bus, +you can disable them like this: .. code-block:: yaml @@ -279,7 +279,7 @@ them like this: messenger: buses: default: - default_middlewares: false + default_middleware: false Your own Transport ------------------ From c76b2c2c6dc22ce55d49ea48b6229960d1ee60c8 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 8 May 2018 21:09:05 +0100 Subject: [PATCH 07/11] Uses the full service name when configuring the buses from the YAML configuration --- messenger.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/messenger.rst b/messenger.rst index 3cf0233105e..a9b7e657768 100644 --- a/messenger.rst +++ b/messenger.rst @@ -195,8 +195,8 @@ this: # Create buses buses: - commands: ~ - events: ~ + messenger.bus.commands: ~ + messenger.bus.events: ~ This will generate the ``messenger.bus.commands`` and ``messenger.bus.events`` services that you can inject in your services. @@ -257,7 +257,7 @@ within the buses to add some extra capabilities like this: framework: messenger: buses: - default: + messenger.bus.default: middleware: # Works with the FQCN if the class discovery is enabled - App\\Middleware\\MyMiddleware @@ -278,7 +278,7 @@ you can disable them like this: framework: messenger: buses: - default: + messenger.bus.default: default_middleware: false Your own Transport From 9b7b85f88398710951d2a4ce7b0808d92fc8c5f2 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 8 May 2018 21:15:35 +0100 Subject: [PATCH 08/11] Update the example of using multiple buses to use DI's `bind`s --- messenger.rst | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/messenger.rst b/messenger.rst index a9b7e657768..dadc3d5eabb 100644 --- a/messenger.rst +++ b/messenger.rst @@ -208,25 +208,19 @@ Auto-wiring is a great feature that allows you to reduce the amount of configura 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 why bus to inject in your own services. -In order to clarify this, you will have to create your own decorators for your message -buses. Let's create one for your ``CommandBus``:: - - namespace App; - - use Symfony\Component\Messenger\AbstractMessageBusDecorator; - - final class CommandBus extends AbstractMessageBusDecorator - { - } - -Last step is to register your service (and explicit its argument) to be able to typehint -your ``CommandBus`` in your 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: .. code-block:: yaml # config/services.yaml services: - App\CommandBus: ['@messenger.bus.commands'] + _defaults: + # ... + + bind: + $commandBus: '@messenger.bus.commands' + $eventBus: '@messenger.bus.events' Middleware ---------- From e1f3b5af19a962c0a19e21439c4ffb9ef76bc074 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 8 May 2018 21:20:41 +0100 Subject: [PATCH 09/11] Fix the formating of the method name --- messenger.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/messenger.rst b/messenger.rst index dadc3d5eabb..1c816f295fd 100644 --- a/messenger.rst +++ b/messenger.rst @@ -44,7 +44,7 @@ Registering Handlers -------------------- In order to do something when your message is dispatched, you need to create a -message handler. It's a class with an `__invoke` method:: +message handler. It's a class with an ``__invoke`` method:: // src/MessageHandler/MyMessageHandler.php namespace App\MessageHandler; @@ -232,7 +232,7 @@ for each bus looks like this: 1. ``logging`` middleware. Responsible of logging the beginning and the end of the message within the bus. -2. _Your own collection of middleware__ +2. _Your own collection of middleware_ 3. ``route_messages`` middleware. Will route the messages your configured to their corresponding sender and stop the middleware chain. @@ -355,4 +355,4 @@ will give you access to the following services: #. ``messenger.sender.yours``: the sender. #. ``messenger.receiver.yours``: the receiver. -.. _`enqueue's transport`: https://github.com/sroze/enqueue-bridge +.. _`enqueue's transport`: https://github.com/enqueue/messenger-adapter From 64bfd75476f4f80b09f8e2e1611ac339aca33439 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 8 May 2018 21:29:39 +0100 Subject: [PATCH 10/11] Change wording and don't use `.`-based services so it's just clearer --- messenger.rst | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/messenger.rst b/messenger.rst index 1c816f295fd..e526bb2543c 100644 --- a/messenger.rst +++ b/messenger.rst @@ -253,13 +253,11 @@ within the buses to add some extra capabilities like this: buses: messenger.bus.default: middleware: - # Works with the FQCN if the class discovery is enabled - - App\\Middleware\\MyMiddleware + - "App\\Middleware\\MyMiddleware" + - "App\\Middleware\\AnotherMiddleware" - # Or with some service name - - app.middleware.yours - -Note that if the service is abstract, then a child service will be created per bus. +Note that if the service is abstract, then a different instance of service will be +created per bus. Disabling default middleware ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From c3c3528a124d3569d6f928aaf9f56d3588c463ff Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 8 May 2018 22:32:15 +0100 Subject: [PATCH 11/11] Few updates following review --- messenger.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/messenger.rst b/messenger.rst index e526bb2543c..ed73d148ae2 100644 --- a/messenger.rst +++ b/messenger.rst @@ -109,7 +109,7 @@ configure the following services for you: .. note:: - In order to use Symfony's built-in AMQP adapter, you will need the Serializer + In order to use Symfony's built-in AMQP transport, you will need the Serializer Component. Ensure that it is installed with: .. code-block:: terminal @@ -253,8 +253,8 @@ within the buses to add some extra capabilities like this: buses: messenger.bus.default: middleware: - - "App\\Middleware\\MyMiddleware" - - "App\\Middleware\\AnotherMiddleware" + - 'App\Middleware\MyMiddleware' + - 'App\Middleware\AnotherMiddleware' Note that if the service is abstract, then a different instance of service will be created per bus.