From 8107340794266a5971c1820660cd357d382ccaa6 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 4 Jul 2018 16:42:01 +0200 Subject: [PATCH 01/13] Added documentation about message recorder --- messenger/message-recorder.rst | 69 ++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 messenger/message-recorder.rst diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst new file mode 100644 index 00000000000..a4610546529 --- /dev/null +++ b/messenger/message-recorder.rst @@ -0,0 +1,69 @@ +.. index:: + single: Messenger; Record messages + +Record Events Produced by a Handler +=================================== + +In a example application there is a command (a CQRS message) named ``CreateUser``. +That command is handled by the ``CreateUserHandler``. The command handler creates +a ``User`` object, stores that object to a database and dispatches an ``UserCreatedEvent``. +That event is also a normal message but is handled by an *event* bus. + +There are many subscribers to the ``UserCreatedEvent``, one subscriber may send +a welcome email to the new user. Since we are using the ``DoctrineTransactionMiddleware`` +we wrap all database queries in one database transaction and rollback that transaction +if an exception is thrown. That would mean that if an exception is thrown when sending +the welcome email, then the user will not be created. + +The solution to this issue is to not dispatch the ``UserCreatedEvent`` in the +``CreateUserHandler`` but to just "record" the events. The recorded events will +be dispatched after ``DoctrineTransactionMiddleware`` has committed the transaction. + +To enable this, you simply just add the ``messenger.middleware.handles_recorded_messages`` +middleware. Make sure it is registered before ``DoctrineTransactionMiddleware`` +in the middleware chain. + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/workflow.yaml + framework: + messenger: + default_bus: messenger.bus.command + buses: + messenger.bus.command: + middleware: + - messenger.middleware.validation + - messenger.middleware.handles_recorded_messages: ['@messenger.bus.event'] + # Doctrine transaction must be after handles_recorded_messages middleware + - app.doctrine_transaction_middleware: ['default'] + messenger.bus.event: + middleware: + - messenger.middleware.allow_no_handler + - messenger.middleware.validation + +.. code-block:: php + + use Doctrine\ORM\EntityManagerInterface; + use Symfony\Component\Messenger\MessageRecorderInterface; + + class CreateUserHandler + { + private $em; + private $eventRecorder; + + public function __construct(MessageRecorderInterface $eventRecorder, EntityManagerInterface $em) + { + $this->eventRecorder = $eventRecorder; + $this->em = $em; + } + + public function __invoke(CreateUser $command) + { + $user = new User($command->getUuid(), $command->getName(), $command->getEmail()); + $this->em->persist($user); + + $this->eventRecorder->record(new UserCreatedEvent($command->getUuid()); + } + } From 66fc600109637e13006440dbf7577dca35e66efb Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Thu, 5 Jul 2018 10:59:58 +0200 Subject: [PATCH 02/13] According to feedback --- messenger/message-recorder.rst | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index a4610546529..4991d7c685b 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -4,15 +4,15 @@ Record Events Produced by a Handler =================================== -In a example application there is a command (a CQRS message) named ``CreateUser``. -That command is handled by the ``CreateUserHandler``. The command handler creates +In an example application there is a command (a CQRS message) named ``CreateUser``. +That command is handled by the ``CreateUserHandler`` which creates a ``User`` object, stores that object to a database and dispatches an ``UserCreatedEvent``. That event is also a normal message but is handled by an *event* bus. There are many subscribers to the ``UserCreatedEvent``, one subscriber may send a welcome email to the new user. Since we are using the ``DoctrineTransactionMiddleware`` we wrap all database queries in one database transaction and rollback that transaction -if an exception is thrown. That would mean that if an exception is thrown when sending +if an exception is thrown. That means that if an exception is thrown when sending the welcome email, then the user will not be created. The solution to this issue is to not dispatch the ``UserCreatedEvent`` in the @@ -27,7 +27,7 @@ in the middleware chain. .. code-block:: yaml - # config/packages/workflow.yaml + # config/packages/messenger.yaml framework: messenger: default_bus: messenger.bus.command @@ -45,6 +45,11 @@ in the middleware chain. .. code-block:: php + namespace App\Messenger\CommandHandler; + + use App\Entity\User; + use App\Messenger\Command\CreateUser; + use App\Messenger\Event\UserCreatedEvent; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Messenger\MessageRecorderInterface; From 091040ff0330805c6633fbfea41e2fa362b863bd Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Thu, 5 Jul 2018 11:02:44 +0200 Subject: [PATCH 03/13] a user --- messenger/message-recorder.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index 4991d7c685b..741db9ec626 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -6,7 +6,7 @@ Record Events Produced by a Handler In an example application there is a command (a CQRS message) named ``CreateUser``. That command is handled by the ``CreateUserHandler`` which creates -a ``User`` object, stores that object to a database and dispatches an ``UserCreatedEvent``. +a ``User`` object, stores that object to a database and dispatches a ``UserCreatedEvent``. That event is also a normal message but is handled by an *event* bus. There are many subscribers to the ``UserCreatedEvent``, one subscriber may send From 0a3ccf6b5b0c829ec82e2764f3ea2f682bbc6f4e Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sun, 26 Aug 2018 18:21:26 +0200 Subject: [PATCH 04/13] Added fixes --- messenger/message-recorder.rst | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index 741db9ec626..92a52bd34d8 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -1,21 +1,23 @@ .. index:: single: Messenger; Record messages -Record Events Produced by a Handler -=================================== +Events Recorder: Handle Events After CommandHandler Is Done +=========================================================== -In an example application there is a command (a CQRS message) named ``CreateUser``. -That command is handled by the ``CreateUserHandler`` which creates -a ``User`` object, stores that object to a database and dispatches a ``UserCreatedEvent``. +Let's take the example of an application that has a command (a CQRS message) named +``CreateUser``. That command is handled by the ``CreateUserHandler`` which creates +a ``User`` object, stores that object to a database and dispatches a ``UserCreated`` event. That event is also a normal message but is handled by an *event* bus. -There are many subscribers to the ``UserCreatedEvent``, one subscriber may send -a welcome email to the new user. Since we are using the ``DoctrineTransactionMiddleware`` -we wrap all database queries in one database transaction and rollback that transaction -if an exception is thrown. That means that if an exception is thrown when sending -the welcome email, then the user will not be created. +There are many subscribers to the ``UserCreated`` event, one subscriber may send +a welcome email to the new user. We are using the ``DoctrineTransactionMiddleware`` +to wrap all database queries in one database transaction. -The solution to this issue is to not dispatch the ``UserCreatedEvent`` in the +**Problem:** If an exception is thrown when sending the welcome email, then the user +will not be created because the ``DoctrineTransactionMiddleware`` will rollback the +Doctrine transaction, in which the user has been created. + +**Solution:** The solution is to not dispatch the ``UserCreated`` event in the ``CreateUserHandler`` but to just "record" the events. The recorded events will be dispatched after ``DoctrineTransactionMiddleware`` has committed the transaction. @@ -69,6 +71,7 @@ in the middleware chain. $user = new User($command->getUuid(), $command->getName(), $command->getEmail()); $this->em->persist($user); + // "Record" this event to be processed later by "handles_recorded_messages". $this->eventRecorder->record(new UserCreatedEvent($command->getUuid()); } } From b5921bf72c1827b1c43e62b5a704615de54998b4 Mon Sep 17 00:00:00 2001 From: Daryl Gubler Date: Sun, 24 Feb 2019 10:13:44 -0500 Subject: [PATCH 05/13] Explain HandleMessageInNewTransaction middleware The current PR is for HandleMessageInNewTransaction middleware instead of the original RecordsMessages middleware. This documentation covers the new middleware. --- messenger/message-recorder.rst | 101 +++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 29 deletions(-) diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index 92a52bd34d8..e4d494df98b 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -1,30 +1,62 @@ .. index:: - single: Messenger; Record messages + single: Messenger; Record messages; Transaction messages -Events Recorder: Handle Events After CommandHandler Is Done -=========================================================== +Transactional Messages: Handle Events After CommandHandler Is Done +================================================================== -Let's take the example of an application that has a command (a CQRS message) named -``CreateUser``. That command is handled by the ``CreateUserHandler`` which creates -a ``User`` object, stores that object to a database and dispatches a ``UserCreated`` event. -That event is also a normal message but is handled by an *event* bus. +A message handler can ``dispatch`` new messages during execution, to either the same or +a different bus (if the application has `multiple buses `_). +Any errors or exceptions that occur during this process can have unintended consequences, +such as: -There are many subscribers to the ``UserCreated`` event, one subscriber may send +- If using the ``DoctrineTransactionMiddleware`` and a dispatched message to the same bus + and an exception is thrown, then any database transactions in the original handler will + be rolled back. +- If the message is dispatched to a different bus, then the dispatched message can still + be handled even if the original handler encounters an exception. + +An Example ``SignUpUser`` Process +--------------------------------- + +Let's take the example of an application with both a *command* and an *event* bus. The application +dispatches a command named ``SignUpUser`` to the command bus. The command is handled by the +``SignUpUserHandler`` which creates a ``User`` object, stores that object to a database and +dispatches a ``UserSignedUp`` event to the event bus. + +There are many subscribers to the ``UserSignedUp`` event, one subscriber may send a welcome email to the new user. We are using the ``DoctrineTransactionMiddleware`` to wrap all database queries in one database transaction. -**Problem:** If an exception is thrown when sending the welcome email, then the user +**Problem 1:** If an exception is thrown when sending the welcome email, then the user will not be created because the ``DoctrineTransactionMiddleware`` will rollback the Doctrine transaction, in which the user has been created. -**Solution:** The solution is to not dispatch the ``UserCreated`` event in the -``CreateUserHandler`` but to just "record" the events. The recorded events will -be dispatched after ``DoctrineTransactionMiddleware`` has committed the transaction. +**Problem 2:** If an exception is thrown when saving the user to the database, the welcome +email is still sent. + +``HandleMessageInNewTransaction`` Middleware +-------------------------------------------- + +For many applications, the desired behavior is to have any messages dispatched by the handler +to `only` be handled after the handler finishes. This can be by using the +``HandleMessageInNewTransaction`` middleware and adding a ``Transaction`` stamp to +`the message Envelope `_. +This middleware enables us to add messages to a separate transaction that will only be +dispatched *after* the current message handler finishes. -To enable this, you simply just add the ``messenger.middleware.handles_recorded_messages`` +Referencing the above example, this means that the ``UserSignedUp`` event would not be handled +until *after* the ``SignUpUserHandler`` had completed and the new ``User`` was persisted to the +database. If the ``SignUpUserHandler`` encounters an exception, the ``UserSignedUp`` event will +never be handled and if an exception is thrown while sending the welcome email, the Doctrine +transaction will not be rolled back. + +To enable this, you need to add the ``handle_message_in_new_transaction`` middleware. Make sure it is registered before ``DoctrineTransactionMiddleware`` in the middleware chain. +**Note:** The ``handle_message_in_new_transaction`` middleware must be loaded for *all* of the +buses. For the example, the middleware must be loaded for both the command and event buses. + .. configuration-block:: .. code-block:: yaml @@ -33,45 +65,56 @@ in the middleware chain. framework: messenger: default_bus: messenger.bus.command + buses: messenger.bus.command: middleware: - - messenger.middleware.validation - - messenger.middleware.handles_recorded_messages: ['@messenger.bus.event'] - # Doctrine transaction must be after handles_recorded_messages middleware - - app.doctrine_transaction_middleware: ['default'] + - validation + - handle_message_in_new_transaction + - doctrine_transaction messenger.bus.event: + default_middleware: allow_no_handlers middleware: - - messenger.middleware.allow_no_handler - - messenger.middleware.validation + - validation + - handle_message_in_new_transaction + - doctrine_transaction + .. code-block:: php namespace App\Messenger\CommandHandler; use App\Entity\User; - use App\Messenger\Command\CreateUser; - use App\Messenger\Event\UserCreatedEvent; + use App\Messenger\Command\SignUpUser; + use App\Messenger\Event\UserSignedUp; use Doctrine\ORM\EntityManagerInterface; - use Symfony\Component\Messenger\MessageRecorderInterface; + use Symfony\Component\Messenger\Envelope; + use Symfony\Component\Messenger\Stamp\Transaction; + use Symfony\Component\Messenger\MessageBusInterface; - class CreateUserHandler + class SignUpUserHandler { private $em; - private $eventRecorder; + private $eventBus; - public function __construct(MessageRecorderInterface $eventRecorder, EntityManagerInterface $em) + public function __construct(MessageBusInterface $eventBus, EntityManagerInterface $em) { - $this->eventRecorder = $eventRecorder; + $this->eventBus = $eventBus; $this->em = $em; } - public function __invoke(CreateUser $command) + public function __invoke(SignUpUser $command) { $user = new User($command->getUuid(), $command->getName(), $command->getEmail()); $this->em->persist($user); - // "Record" this event to be processed later by "handles_recorded_messages". - $this->eventRecorder->record(new UserCreatedEvent($command->getUuid()); + // The Transaction stamp marks the event message to be handled + // only if this handler does not throw an exception. + + $event = new UserSignedUp($command->getUuid()); + $this->eventBus->dispatch( + (new Envelope($event)) + ->with(new Transaction()) + ); } } From aba0edf038302a9fbcf2292a6167bcd42f4d4640 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sun, 17 Mar 2019 11:41:48 +0100 Subject: [PATCH 06/13] Minor updates --- messenger/message-recorder.rst | 63 ++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index e4d494df98b..3d92a04a23f 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -9,9 +9,8 @@ a different bus (if the application has `multiple buses `_. -This middleware enables us to add messages to a separate transaction that will only be -dispatched *after* the current message handler finishes. +``DispatchAfterCurrentBusMiddleware`` middleware and adding a ``DispatchAfterCurrentBusStamp`` +stamp to `the message Envelope `_. Referencing the above example, this means that the ``UserSignedUp`` event would not be handled until *after* the ``SignUpUserHandler`` had completed and the new ``User`` was persisted to the @@ -50,11 +47,12 @@ database. If the ``SignUpUserHandler`` encounters an exception, the ``UserSigned never be handled and if an exception is thrown while sending the welcome email, the Doctrine transaction will not be rolled back. -To enable this, you need to add the ``handle_message_in_new_transaction`` -middleware. Make sure it is registered before ``DoctrineTransactionMiddleware`` +The ``dispatch_after_current_bus`` middleware is enabled by default. It is configured as the +first middleware on all busses. When doing a highly custom or special configuration, then make +sure ``dispatch_after_current_bus`` is registered before ``doctrine_transaction`` in the middleware chain. -**Note:** The ``handle_message_in_new_transaction`` middleware must be loaded for *all* of the +**Note:** The ``dispatch_after_current_bus`` middleware must be loaded for *all* of the buses. For the example, the middleware must be loaded for both the command and event buses. .. configuration-block:: @@ -70,13 +68,11 @@ buses. For the example, the middleware must be loaded for both the command and e messenger.bus.command: middleware: - validation - - handle_message_in_new_transaction - doctrine_transaction messenger.bus.event: default_middleware: allow_no_handlers middleware: - validation - - handle_message_in_new_transaction - doctrine_transaction @@ -89,7 +85,7 @@ buses. For the example, the middleware must be loaded for both the command and e use App\Messenger\Event\UserSignedUp; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Messenger\Envelope; - use Symfony\Component\Messenger\Stamp\Transaction; + use Symfony\Component\Messenger\Stamp\DispatchAfterCurrentBusStamp; use Symfony\Component\Messenger\MessageBusInterface; class SignUpUserHandler @@ -108,13 +104,44 @@ buses. For the example, the middleware must be loaded for both the command and e $user = new User($command->getUuid(), $command->getName(), $command->getEmail()); $this->em->persist($user); - // The Transaction stamp marks the event message to be handled + // The DispatchAfterCurrentBusStamp marks the event message to be handled // only if this handler does not throw an exception. $event = new UserSignedUp($command->getUuid()); $this->eventBus->dispatch( (new Envelope($event)) - ->with(new Transaction()) + ->with(new DispatchAfterCurrentBusStamp()) ); } } + +.. code-block:: php + + namespace App\Messenger\EventSubscriber; + + use App\Entity\User; + use App\Messenger\Event\UserSignedUp; + use Doctrine\ORM\EntityManagerInterface; + + class WhenUserSignedUpThenSendWelcomeEmail + { + private $em; + private $mailer; + + public function __construct(MyMailer $mailer, EntityManagerInterface $em) + { + $this->mailer = $mailer; + $this->em = $em; + } + + public function __invoke(UserSignedUp $eent) + { + $user = $this->em->getRepository(User::class)->find(new User($event->getUuid())); + + $this->mailer->sendWelcomeEmail($user); + } + } + +**Note:** If ``WhenUserSignedUpThenSendWelcomeEmail`` throws an exception, that exception +will be wrapped into a ``DelayedMessageHandlingException``. Using ``DelayedMessageHandlingException::getExceptions`` +will give you all exceptions that are thrown while handing a message with the ``DispatchAfterCurrentBusStamp``. From 9b589ff6c81f9972504147f598e4c67f7a6e7c1b Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Mon, 8 Apr 2019 20:52:51 +0200 Subject: [PATCH 07/13] Added PHP and XML config --- messenger/message-recorder.rst | 43 +++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index 3d92a04a23f..e2f8fb97810 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -53,7 +53,7 @@ sure ``dispatch_after_current_bus`` is registered before ``doctrine_transaction` in the middleware chain. **Note:** The ``dispatch_after_current_bus`` middleware must be loaded for *all* of the -buses. For the example, the middleware must be loaded for both the command and event buses. +buses. For the example, the middleware must be loaded for both the command and event bus. .. configuration-block:: @@ -75,6 +75,47 @@ buses. For the example, the middleware must be loaded for both the command and e - validation - doctrine_transaction + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + .. code-block:: php + + // config/packages/cache.php + $container->loadFromExtension('framework', [ + 'messenger' => [ + 'default_bus' => 'messenger.bus.command', + 'buses' => [ + 'messenger.bus.command' => [ + 'middleware' => ['validation', 'doctrine_transaction'], + ], + 'messenger.bus.event' => [ + 'default_middleware' => 'allow_no_handlers', + 'middleware' => ['validation', 'doctrine_transaction'], + ], + ], + ], + ]); .. code-block:: php From 0e1e0dd5271f71685f7ac4c8f40423025c4fafde Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 8 Apr 2019 21:37:24 +0200 Subject: [PATCH 08/13] Update messenger/message-recorder.rst Co-Authored-By: Nyholm --- messenger/message-recorder.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index e2f8fb97810..d6aff9080ff 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -1,7 +1,7 @@ .. index:: single: Messenger; Record messages; Transaction messages -Transactional Messages: Handle Events After CommandHandler Is Done +Transactional Messages: Handle Events After CommandHandler is Done ================================================================== A message handler can ``dispatch`` new messages during execution, to either the same or From aa4096f5ad044f512e818051bd760993cda818b2 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 8 Apr 2019 21:38:11 +0200 Subject: [PATCH 09/13] Update messenger/message-recorder.rst Co-Authored-By: Nyholm --- messenger/message-recorder.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index d6aff9080ff..8de5aa5d01c 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -77,7 +77,7 @@ buses. For the example, the middleware must be loaded for both the command and e .. code-block:: xml - + Date: Mon, 8 Apr 2019 21:40:06 +0200 Subject: [PATCH 10/13] Update messenger/message-recorder.rst Co-Authored-By: Nyholm --- messenger/message-recorder.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index 8de5aa5d01c..bf6c8899601 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -101,7 +101,7 @@ buses. For the example, the middleware must be loaded for both the command and e .. code-block:: php - // config/packages/cache.php + // config/packages/messenger.php $container->loadFromExtension('framework', [ 'messenger' => [ 'default_bus' => 'messenger.bus.command', From 157d3f01ea3f0af78b131ee0eafbd11ac296eb6b Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 8 Apr 2019 21:40:30 +0200 Subject: [PATCH 11/13] Update messenger/message-recorder.rst Co-Authored-By: Nyholm --- messenger/message-recorder.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index bf6c8899601..8b7a27af6dd 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -175,7 +175,7 @@ buses. For the example, the middleware must be loaded for both the command and e $this->em = $em; } - public function __invoke(UserSignedUp $eent) + public function __invoke(UserSignedUp $event) { $user = $this->em->getRepository(User::class)->find(new User($event->getUuid())); From 6b71ef82ddfa0b7a5e5a29b64a18b204658d1afc Mon Sep 17 00:00:00 2001 From: Nyholm Date: Mon, 8 Apr 2019 22:01:45 +0200 Subject: [PATCH 12/13] updates according to feedback --- messenger/message-recorder.rst | 52 ++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index 8b7a27af6dd..63018e4125f 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -14,15 +14,15 @@ such as: - If the message is dispatched to a different bus, then the dispatched message can still be handled even if the original handler encounters an exception. -An Example ``SignUpUser`` Process ---------------------------------- +An Example ``RegisterUser`` Process +----------------------------------- Let's take the example of an application with both a *command* and an *event* bus. The application -dispatches a command named ``SignUpUser`` to the command bus. The command is handled by the -``SignUpUserHandler`` which creates a ``User`` object, stores that object to a database and -dispatches a ``UserSignedUp`` event to the event bus. +dispatches a command named ``RegisterUser`` to the command bus. The command is handled by the +``RegisterUserHandler`` which creates a ``User`` object, stores that object to a database and +dispatches a ``UserRegistered`` event to the event bus. -There are many subscribers to the ``UserSignedUp`` event, one subscriber may send +There are many subscribers to the ``UserRegistered`` event, one subscriber may send a welcome email to the new user. We are using the ``DoctrineTransactionMiddleware`` to wrap all database queries in one database transaction. @@ -41,9 +41,9 @@ to `only` be handled after the handler finishes. This can be by using the ``DispatchAfterCurrentBusMiddleware`` middleware and adding a ``DispatchAfterCurrentBusStamp`` stamp to `the message Envelope `_. -Referencing the above example, this means that the ``UserSignedUp`` event would not be handled -until *after* the ``SignUpUserHandler`` had completed and the new ``User`` was persisted to the -database. If the ``SignUpUserHandler`` encounters an exception, the ``UserSignedUp`` event will +Referencing the above example, this means that the ``UserRegistered`` event would not be handled +until *after* the ``RegisterUserHandler`` had completed and the new ``User`` was persisted to the +database. If the ``RegisterUserHandler`` encounters an exception, the ``UserRegistered`` event will never be handled and if an exception is thrown while sending the welcome email, the Doctrine transaction will not be rolled back. @@ -122,17 +122,17 @@ buses. For the example, the middleware must be loaded for both the command and e namespace App\Messenger\CommandHandler; use App\Entity\User; - use App\Messenger\Command\SignUpUser; - use App\Messenger\Event\UserSignedUp; + use App\Messenger\Command\RegisterUser; + use App\Messenger\Event\UserRegistered; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Stamp\DispatchAfterCurrentBusStamp; use Symfony\Component\Messenger\MessageBusInterface; - class SignUpUserHandler + class RegisterUserHandler { - private $em; private $eventBus; + private $em; public function __construct(MessageBusInterface $eventBus, EntityManagerInterface $em) { @@ -140,7 +140,7 @@ buses. For the example, the middleware must be loaded for both the command and e $this->em = $em; } - public function __invoke(SignUpUser $command) + public function __invoke(RegisterUser $command) { $user = new User($command->getUuid(), $command->getName(), $command->getEmail()); $this->em->persist($user); @@ -148,7 +148,7 @@ buses. For the example, the middleware must be loaded for both the command and e // The DispatchAfterCurrentBusStamp marks the event message to be handled // only if this handler does not throw an exception. - $event = new UserSignedUp($command->getUuid()); + $event = new UserRegistered($command->getUuid()); $this->eventBus->dispatch( (new Envelope($event)) ->with(new DispatchAfterCurrentBusStamp()) @@ -161,28 +161,32 @@ buses. For the example, the middleware must be loaded for both the command and e namespace App\Messenger\EventSubscriber; use App\Entity\User; - use App\Messenger\Event\UserSignedUp; + use App\Messenger\Event\UserRegistered; use Doctrine\ORM\EntityManagerInterface; + use Symfony\Component\Mailer\MailerInterface; + use Symfony\Component\Mime\RawMessage; - class WhenUserSignedUpThenSendWelcomeEmail + class WhenUserRegisteredThenSendWelcomeEmail { - private $em; private $mailer; + private $em; - public function __construct(MyMailer $mailer, EntityManagerInterface $em) + public function __construct(MailerInterface $mailer, EntityManagerInterface $em) { $this->mailer = $mailer; $this->em = $em; } - public function __invoke(UserSignedUp $event) + public function __invoke(UserRegistered $event) { $user = $this->em->getRepository(User::class)->find(new User($event->getUuid())); - $this->mailer->sendWelcomeEmail($user); + $this->mailer->send(new RawMessage('Welcome '.$user->getFirstName())); } } -**Note:** If ``WhenUserSignedUpThenSendWelcomeEmail`` throws an exception, that exception -will be wrapped into a ``DelayedMessageHandlingException``. Using ``DelayedMessageHandlingException::getExceptions`` -will give you all exceptions that are thrown while handing a message with the ``DispatchAfterCurrentBusStamp``. +.. note:: + + If ``WhenUserRegisteredThenSendWelcomeEmail`` throws an exception, that exception + will be wrapped into a ``DelayedMessageHandlingException``. Using ``DelayedMessageHandlingException::getExceptions`` + will give you all exceptions that are thrown while handing a message with the ``DispatchAfterCurrentBusStamp``. From 45245dfcdfd785a01ba7346b568b0f99609061d7 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sun, 28 Apr 2019 09:14:00 +0200 Subject: [PATCH 13/13] minor updates according to feedback --- messenger/message-recorder.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index 63018e4125f..2a95e5b8bfc 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -11,8 +11,8 @@ such as: - If using the ``DoctrineTransactionMiddleware`` and a dispatched message throws an exception, then any database transactions in the original handler will be rolled back. -- If the message is dispatched to a different bus, then the dispatched message can still - be handled even if the original handler encounters an exception. +- If the message is dispatched to a different bus, then dispatched message will be + handled even if the current handler throws an exception. An Example ``RegisterUser`` Process ----------------------------------- @@ -68,12 +68,10 @@ buses. For the example, the middleware must be loaded for both the command and e messenger.bus.command: middleware: - validation - - doctrine_transaction messenger.bus.event: default_middleware: allow_no_handlers middleware: - validation - - doctrine_transaction .. code-block:: xml @@ -153,6 +151,8 @@ buses. For the example, the middleware must be loaded for both the command and e (new Envelope($event)) ->with(new DispatchAfterCurrentBusStamp()) ); + + // ... } }