Skip to content

Commit

Permalink
minor #9437 Symfony Messenger component documentation (sroze, javiere…
Browse files Browse the repository at this point in the history
…guiluz, wiese)

This PR was merged into the master branch.

Discussion
----------

Symfony Messenger component documentation

This pull-request adds some documentation for the Messenger component. As it is, it's extracted from the README built incrementally in the code pull-request.

I'm sure there will be lots to change when the implementation is merged :)

Commits
-------

c5306b8 Minor wording change
32403ea Update the documentation to reflect the latest changes
3fb973c Update based on comments
509e149 Add a documentation about middlewares and update based on reviews
fb88abc Add Messenger in the topics
a15752b Fix unresolved reference
25c0b6e It helps multiple applications  Please enter the commit message for your changes. Lines starting
bcfae23 Finish the documentation about the custom adapter
2493c90 Update typos and missing reference
31a56ee Uses `::` to display as code blocks
5c828e4 Update the documentation a bit, and add more FrameworkBundle documentation
88ba8fe Merge pull request #1 from wiese/patch-3
b26de80 minor doc fixes for the Message component
b40bc71 Fixed the RST syntax issues
585491d Move the documentation from the main PR to RST format.
  • Loading branch information
javiereguiluz committed Apr 16, 2018
2 parents 44f1f4d + c5306b8 commit db9d8e1
Show file tree
Hide file tree
Showing 4 changed files with 426 additions and 0 deletions.
Binary file added _images/components/messenger/overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
192 changes: 192 additions & 0 deletions components/messenger.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
.. index::
single: Messenger
single: Components; Messenger

The Messenger Component
=======================

The Messenger component helps applications send and receive messages to/from other applications or via message queues.

Installation
------------

.. code-block:: terminal
$ composer require symfony/messenger
Alternatively, you can clone the `<https://github.com/symfony/messenger>`_ repository.

.. include:: /components/require_autoload.rst.inc

Concepts
--------

.. image:: /_images/components/messenger/overview.png

**Sender**:
Responsible for serializing and sending messages to _something_. This
something can be a message broker or a third party API for example.

**Receiver**:
Responsible for deserializing and forwarding messages to handler(s). This
can be a message queue puller or an API endpoint for example.

**Handler**:
Responsible for handling messages using the business logic applicable to the messages.

Bus
---

The bus is used to dispatch messages. The behaviour of the bus is in its ordered
middleware stack. The component comes with a set of middleware that you can use.

When using the message bus with Symfony's FrameworkBundle, the following middleware
are configured for you:

#. ``LoggingMiddleware`` (logs the processing of your messages)
#. ``SendMessageMiddleware`` (enables asynchronous processing)
#. ``HandleMessageMiddleware`` (calls the registered handle)

Example::

use App\Message\MyMessage;
use Symfony\Component\Messenger\MessageBus;
use Symfony\Component\Messenger\HandlerLocator;
use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware;

$bus = new MessageBus([
new HandleMessageMiddleware(new HandlerLocator([
MyMessage::class => $handler,
])),
]);

$result = $bus->handle(new MyMessage(/* ... */));

.. note:
Every middleware needs to implement the ``MiddlewareInterface`` interface.
Handlers
--------

Once dispatched to the bus, messages will be handled by a "message handler". A
message handler is a PHP callable (i.e. a function or an instance of a class)
that will do the required processing for your message::

namespace App\MessageHandler;

use App\Message\MyMessage;

class MyMessageHandler
{
public function __invoke(MyMessage $message)
{
// Message processing...
}
}

Adapters
--------

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.

Your own sender
~~~~~~~~~~~~~~~

Using the ``SenderInterface``, you can easily create your own message sender.
Let's say you already have an ``ImportantAction`` message going through the
message bus and handled by a handler. Now, you also want to send this message as
an email.

First, create your sender::

namespace App\MessageSender;

use App\Message\ImportantAction;
use Symfony\Component\Message\SenderInterface;

class ImportantActionToEmailSender implements SenderInterface
{
private $toEmail;
private $mailer;

public function __construct(\Swift_Mailer $mailer, string $toEmail)
{
$this->mailer = $mailer;
$this->toEmail = $toEmail;
}

public function send($message)
{
if (!$message instanceof ImportantAction) {
throw new \InvalidArgumentException(sprintf('Producer only supports "%s" messages.', ImportantAction::class));
}

$this->mailer->send(
(new \Swift_Message('Important action made'))
->setTo($this->toEmail)
->setBody(
'<h1>Important action</h1><p>Made by '.$message->getUsername().'</p>',
'text/html'
)
);
}
}

Your own receiver
~~~~~~~~~~~~~~~~~

A receiver is responsible for receiving messages from a source and dispatching
them to the application.

Let's say you already processed some "orders" in your application using a
``NewOrder`` message. Now you want to integrate with a 3rd party or a legacy
application but you can't use an API and need to use a shared CSV file with new
orders.

You will read this CSV file and dispatch a ``NewOrder`` message. All you need to
do is to write your custom CSV receiver and Symfony will do the rest.

First, create your receiver::

namespace App\MessageReceiver;

use App\Message\NewOrder;
use Symfony\Component\Message\ReceiverInterface;
use Symfony\Component\Serializer\SerializerInterface;

class NewOrdersFromCsvFile implements ReceiverInterface
{
private $serializer;
private $filePath;

public function __construct(SerializerInteface $serializer, string $filePath)
{
$this->serializer = $serializer;
$this->filePath = $filePath;
}

public function receive(callable $handler) : void
{
$ordersFromCsv = $this->serializer->deserialize(file_get_contents($this->filePath), 'csv');

foreach ($ordersFromCsv as $orderFromCsv) {
$handler(new NewOrder($orderFromCsv['id'], $orderFromCsv['account_id'], $orderFromCsv['amount']));
}
}

public function stop(): void
{
// noop
}
}

Receiver and Sender on the same bus
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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.
1 change: 1 addition & 0 deletions index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Topics
frontend
http_cache
logging
messenger
performance
profiler
routing
Expand Down
Loading

0 comments on commit db9d8e1

Please sign in to comment.