|
| 1 | +# Symfony Messenger Integration: CQRS and Async Message Processing |
| 2 | + |
| 3 | +API Platform provides an integration with the [Symfony Messenger Component](https://symfony.com/doc/current/messenger.html). |
| 4 | + |
| 5 | +This feature allows to implement the [Command Query Responsibility Segregation (CQRS)](https://martinfowler.com/bliki/CQRS.html) pattern in a convenient way. |
| 6 | +It also makes it easy to send messages through the web API that will be consumed asynchronously. |
| 7 | + |
| 8 | +Many transports are supported to dispatch messages to async consumers, including RabbitMQ, Apache Kafka, Amazon SQS and Google Pub/Sub. |
| 9 | + |
| 10 | +## Installing Symfony Messenger |
| 11 | + |
| 12 | +To enable the support of Messenger, install the library: |
| 13 | + |
| 14 | + docker-compose exec php composer require messenger |
| 15 | + |
| 16 | +## Dispatching a Resource through the Message Bus |
| 17 | + |
| 18 | +Set the `messenger` attribute to `true`, and API Platform will automatically dispatch the API Resource instance as a message using the message bus provided by the Messenger Component: |
| 19 | + |
| 20 | +```php |
| 21 | +<?php |
| 22 | + |
| 23 | +// api/src/Entity/ResetPasswordRequest.php |
| 24 | + |
| 25 | +namespace App\Entity; |
| 26 | + |
| 27 | +use ApiPlatform\Core\Annotation\ApiResource; |
| 28 | +use Symfony\Component\Validator\Constraints as Assert; |
| 29 | + |
| 30 | +/** |
| 31 | + * @ApiResource( |
| 32 | +* messenger=true, |
| 33 | + * collectionOperations={ |
| 34 | + * "post"={"status"=202} |
| 35 | + * }, |
| 36 | + * itemOperations={}, |
| 37 | + * outputClass=false |
| 38 | + * ) |
| 39 | + */ |
| 40 | +final class ResetPasswordRequest |
| 41 | +{ |
| 42 | + /** |
| 43 | + * @var string |
| 44 | + * |
| 45 | + * @Assert\NotBlank |
| 46 | + */ |
| 47 | + public $username; |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +Because the `messenger` attribute is true, when a `POST` will be handled by API Platform, the corresponding instance of the `ResetPasswordRequest` will be dispatched. |
| 52 | + |
| 53 | +For this example, only the `POST` operation is enabled. |
| 54 | +We use the `status` attribute to configure API Platform to return a [202 Accepted HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/202). |
| 55 | +It indicates that the request has been received and will be treated later, without giving an immediate return to the client. |
| 56 | +Finally, the `output_class` attribute is set to `false`, so the HTTP response that will be generated by API Platform will be empty, and the [serialization process](serialization.md) will be skipped. |
| 57 | + |
| 58 | +## Registering a Message Handler |
| 59 | + |
| 60 | +To process the message that will be dispatched, [a handler](https://symfony.com/doc/current/messenger.html#registering-handlers) must be created: |
| 61 | + |
| 62 | +```php |
| 63 | +<?php |
| 64 | + |
| 65 | +// api/src/Handler/ResetPasswordRequestHandler.php |
| 66 | + |
| 67 | +namespace App\Handler; |
| 68 | + |
| 69 | +use Symfony\Component\Messenger\Handler\MessageHandlerInterface; |
| 70 | + |
| 71 | +final class ResetPasswordRequestHandler implements MessageHandlerInterface |
| 72 | +{ |
| 73 | + public function __invoke(ResetPasswordRequest $forgotPassword) |
| 74 | + { |
| 75 | + // do something with the resource |
| 76 | + } |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +That's all! |
| 81 | + |
| 82 | +By default, the handler will be processed synchronously your message. |
| 83 | +If you want them to be consumed asynchronously (e.g. by on a worker machine), don't forget [to configure a transport and to execute the consumer](https://symfony.com/doc/current/messenger.html#transports). |
| 84 | + |
| 85 | +## Accessing to the Data Returned by the Handler |
| 86 | + |
| 87 | +API Platform automatically uses the `Symfony\Component\Messenger\Stamp\HandledStamp` when set. |
| 88 | +It means that if you use a synchronous worker, the data returned by the `__invoke` method of your handler replaces the original data. |
| 89 | + |
| 90 | +## Detecting Removals |
| 91 | + |
| 92 | +If the Messenger integration is enabled, API Platform automatically adds a `ApiPlatform\Core\Bridge\Symfony\Messenger\RemoveStamp` ["stamp"](https://symfony.com/doc/current/components/messenger.html#adding-metadata-to-messages-envelopes) instance to the "envelope" when a `DELETE` operation occurs. |
| 93 | +You can check for the presence of this stamp using a custom "middleware" to differentiate typical persists calls (create and update) and removal calls. |
0 commit comments