Skip to content

Commit 41762de

Browse files
committed
[Messenger] Added a trait for synchronous query & command buses
1 parent bd21549 commit 41762de

File tree

3 files changed

+141
-0
lines changed

3 files changed

+141
-0
lines changed

components/messenger.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,5 +298,14 @@ loop, the message bus will add a :class:`Symfony\\Component\\Messenger\\Stamp\\R
298298
stamp to the message envelopes and the :class:`Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware`
299299
middleware will know it should not route these messages again to a transport.
300300

301+
Learn more
302+
----------
303+
.. toctree::
304+
:maxdepth: 1
305+
:glob:
306+
307+
/messenger
308+
/messenger/*
309+
301310
.. _blog posts about command buses: https://matthiasnoback.nl/tags/command%20bus/
302311
.. _SimpleBus project: http://simplebus.io

messenger.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,4 +928,12 @@ will give you access to the following services:
928928
#. ``messenger.sender.yours``: the sender;
929929
#. ``messenger.receiver.yours``: the receiver.
930930

931+
Learn more
932+
----------
933+
.. toctree::
934+
:maxdepth: 1
935+
:glob:
936+
937+
/messenger/*
938+
931939
.. _`enqueue's transport`: https://github.com/php-enqueue/messenger-adapter

messenger/handler_results.rst

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
.. index::
2+
single: Messenger; Getting results / Working with command & query buses
3+
4+
Getting results
5+
---------------
6+
7+
When a message is handled, the :class:`Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware`
8+
adds a :class:`Symfony\\Component\\Messenger\\Stamp\\HandledStamp` for each responding handler,
9+
which can be used to get their returned value:
10+
11+
.. configuration-block::
12+
13+
.. code-block:: php
14+
15+
use Symfony\Component\Messenger\MessageBusInterface;
16+
use Symfony\Component\Messenger\Stamp\HandledStamp;
17+
18+
$envelope = $messageBus->dispatch(SomeMessage());
19+
20+
// Get the last handled stamp and its returned value:
21+
$handledStamp = $envelope->last(HandledStamp::class);
22+
$handledStamp->getResult();
23+
24+
This assumes there is only one configured handler or that only the last handler
25+
result is relevant. But you can also get the stamps for all handlers:
26+
27+
.. configuration-block::
28+
29+
.. code-block:: php
30+
31+
use Symfony\Component\Messenger\MessageBusInterface;
32+
use Symfony\Component\Messenger\Stamp\HandledStamp;
33+
34+
$envelope = $messageBus->dispatch(SomeMessage());
35+
$handledStamps = $envelope->all(HandledStamp::class);
36+
37+
A :class:`Symfony\\Component\\Messenger\\HandleTrait` also exists in order to ease
38+
leveraging a Messenger bus for synchronous needs.
39+
The :method:`Symfony\\Component\\Messenger\\HandleTrait::handle` method ensures
40+
there is exactly one handler registered and returns its result.
41+
42+
Working with command & query buses
43+
----------------------------------
44+
45+
The Messenger component can be used in CQRS architectures where command & query
46+
buses are central pieces of the application.
47+
See Martin Fowler's `article about CQRS`_ to learn more and :doc:`how to configure multiple buses </messenger/multiple_buses>`.
48+
49+
As queries are usually synchronous and expect to be handled once, getting the
50+
result from the handler is a common need.
51+
52+
You can directly leverage the ``HandleTrait`` trait in an action (or any caller):
53+
54+
.. configuration-block::
55+
56+
.. code-block:: php
57+
58+
use App\Message\ListItemsQuery;
59+
use App\MessageHandler\ListItemsQueryResult;
60+
use Symfony\Component\Messenger\HandleTrait;
61+
use Symfony\Component\Messenger\MessageBusInterface;
62+
63+
class ListItemsAction
64+
{
65+
use HandleTrait;
66+
67+
public function __construct(MessageBusInterface $messageBus)
68+
{
69+
$this->messageBus = $messageBus;
70+
}
71+
72+
public function __invoke(Request $request)
73+
{
74+
// Extract data from the request
75+
//...
76+
77+
$result = $this->query(new ListItemsQuery(/* ... */));
78+
79+
// Return a response with result
80+
// ...
81+
}
82+
83+
// Creating such a method is optional, but allows type-hinting the result
84+
private function query(ListItemsQuery $query): ListItemsResult
85+
{
86+
return $this->handle($query);
87+
}
88+
}
89+
90+
Or you can use the trait to create command & query buses classes.
91+
Here is how you can create a ``QueryBus`` class to inject in places you need
92+
a query bus behavior instead of the ``MessageBusInterface``:
93+
94+
.. configuration-block::
95+
96+
.. code-block:: php
97+
98+
namespace App\MessageBus;
99+
100+
use Symfony\Component\Messenger\Envelope;
101+
use Symfony\Component\Messenger\HandleTrait;
102+
use Symfony\Component\Messenger\MessageBusInterface;
103+
104+
class QueryBus
105+
{
106+
use HandleTrait;
107+
108+
public function __construct(MessageBusInterface $messageBus)
109+
{
110+
$this->messageBus = $messageBus;
111+
}
112+
113+
/**
114+
* @param object|Envelope $command
115+
*
116+
* @return mixed The handler returned value
117+
*/
118+
public function query($query)
119+
{
120+
return $this->handle($query);
121+
}
122+
}
123+
124+
.. _article about CQRS: https://martinfowler.com/bliki/CQRS.html

0 commit comments

Comments
 (0)