Collecting events and the event dispatching command bus

Posted on by Matthias Noback

It was quite a ride so far. We have seen commands, command buses, events and event buses. We distilled some more knowledge about them while formulating answers to some interesting questions from readers.

Why you should not dispatch events while handling a command

In a previous post we discussed a sample event (the UserSignedUp event):

class UserSignedUp implements Event
{
    public function name()
    {
        return 'user_signed_up';
    }

    public function __construct($userId)
    {
        $this->userId = $userId;
    }

    public function userId()
    {
        return $this->userId;
    }
}

An instance of such an event can be handed over to the event bus. It will look for any number of event handlers that wants to be notified about the event. In the case of the UserSignedUp event, one of the interested event handlers is the SendWelcomeMailWhenUserSignedUp handler:

class SendWelcomeMailWhenUserSignedUp implements EventHandler
{
    ...

    public function handle(Event $event)
    {
        $user = $this->userRepository->getById($event->userId());

        $this->userMailer->sendWelcomeMailTo($user);
    }
}

The UserSignedUp event occurs whenever a SignUpUser command has been handled. We might be tempted to inject the event bus into the command handler and let it handle the event immediately:

class SignUpHandler
{
    public function __construct(EventBus $eventBus, ...)
    {
        $this->eventBus = $eventBus;
        ...
    }

    public function handle($command)
    {
        ...

        $this->userRepository->add($user);

        $event = new UserSignedUp($user->id());

        $this->eventBus->handle($event);
    }
}

We should not forget however that this command is handled within a database transaction created by one of the command buses. And as I already mentioned, we don't want the event to be handled within the same transaction, since it could endanger the success of that transaction. Also, when the transaction fails in the end, we might end up handling an event for entities whose actual state is insecure.

So instead, we should merely collect events while handling the command and only hand them over to the event bus when the transaction was committed, i.e. when we know that the command has been successfully and completely handled.

Event providers

Of course, SimpleBus would not be complete without an out-of-the-box implementation of this kind of delayed event handling. The connecting concept between events and the command bus is the ProvidesEvents interface, which is part of the SimpleBus/EventBus package.

namespace SimpleBus\Event\Provider;

use SimpleBus\Event\Event;

interface ProvidesEvents
{
    /**
     * @return Event[]
     */
    public function releaseEvents();
}

You can provide a simple implementation for such an EventProvider by defining a class for it, which uses the trait EventProviderCapabilities. This adds the ability to collect events (using the raise() method) and release them later, using the releaseEvents() method:

use SimpleBus\Event\Provider\EventProviderCapabilities;

class EventProvider implements ProvidesEvents
{
    use EventProviderCapabilities;
}

We could inject this EventProvider into any command handler and use the raise() method of the EventProvider to raise new events (which will not be handled right-away, but only stored in-memory):

class SignUpHandler
{
    public function __construct(EventProvider $eventProvider, ...)
    {
        $this->eventProvider = $eventProvider;
        ...
    }

    public function handle($command)
    {
        ...

        $event = new UserSignedUp($user->id());

        $this->eventProvider->raise($event);
    }
}

Now, at what point could we safely release the events collected by the EventProvider and hand those events over to the event bus? The answer is: in yet another command bus! We simply wrap the new event-aware command bus which starts and commits the database transaction. Only then can we be sure that the transaction was successful and we can proceed to safely dispatch the collected events.

Since SimpleBus/CommandBus and SimpleBus/EventBus are stand-alone packages, I introduced a bridge package between the two, SimpleBus/CommandEventBridge. It contains this class, which is a CommandBus, but asks the event provider to release its events, and lets the EventBus instance handle them:

class DispatchesEvents implements CommandBus
{
    use RemembersNext;

    private $eventProvider;
    private $eventBus;

    public function __construct(ProvidesEvents $eventProvider, EventBus $eventBus)
    {
        $this->eventBus = $eventBus;
        $this->eventProvider = $eventProvider;
    }

    public function handle(Command $command)
    {
        $this->next($command);

        foreach ($this->eventProvider->releaseEvents() as $event) {
            $this->eventBus->handle($event);
        }
    }
}

In a diagram this would look something like this (from left to right it shows you the passage of time):

Diagram

Conclusion

After reading this series about command and event buses, you can start using commands to interact with your application and events to act upon the things that happened. Because both the command and the event bus are highly extensible you have many options for introducing more advanced behavior. You could think of:

  • Collecting events in an store
  • Handling commands asynchronously
  • The same for events
  • ...

For now we are more or less done talking about command and event buses. If you have questions, just write a comment below this post. I'm currently working on version 2.0 of the SimpleBus packages. You can at least expect a new post when I'm done, to announce the changes with respect to 1.0. To give you a sneak peek:

  • Code for command and event buses will be generalized and bundled in the MessageBus package.
  • Instead of wrapping message buses using composition, this new package introduces the concept of message bus middlewares, offering greater flexibility and cleaner code for your own specialized message bus features.
PHP hexagonal architecture commands command bus events event bus SimpleBus
Comments
This website uses MailComments: you can send your comments to this post by email. Read more about MailComments, including suggestions for writing your comments (in HTML or Markdown).
musicvicious

Hey Matthias,

What about raising events in the Entity rather than in the Command?
How would you go about it using the EventProvider?

I am thinking, maybe we raise the events in the entity and store them in an array. In the Command Handler we can then use the EventProvider to collect the events from the Entity.

What do you think?
Thanks

Matthias Noback

Certainly, that's how I do it these days: an entity collecs the events, you take them out and dispatch them. SimpleBus does this by the way, if you use the DoctrineBridge. But you can easily do it without a bus.

Hi, I've seen code where they add events to an array within an entity and are dispatched later on. Where is this talked about ? any chance you could get the URL of that post ? thank you very much !

musicvicious

Actually, I've read about it in this actual post :)) I've read so many stuff lately that i forgot where I got the knowledge from.
Cheers!

musicvicious

Thanks for the quick reply!
I am using Tactician for my command bus implementation, should be easy to switch to SimpleBus. I use a transaction middleware that also takes care of flushing and I want to add one more in the chain to take care of the event dispatching. I think I've read about that in another one of your posts, really helpful!
Thanks a lot!

Matthias Noback

Nice :) Well, I'm sure you can replicate this behavior with Tactician as well, and if I remember correctly, there's a similar Doctrine-plugin for Tactician too.

Dejan

Hello, is there any special reason why the SimpleBus/CommandEventBridge package is now deprecated?

S├ębastien Grans

Hi, the reason why is that the CommandBus and EventBus merged in one package (message-bus) (because both using the same base class, MessageBusSupportingMiddleware). Here a example of a custom bus with support of events and asynchronous handling with bernard: https://gist.github.com/Axx...

Jose Celano

Hi Matthias. I have seen you use these three class names for event suscribers:

SendWelcomeMailWhenUserSignedUp
UserRegisteredEventSubscriber
UserRegisteredEventsHandler

I think the first is better but probably we would have a a lot of EventHandlers. One for each secondary task (http://php-and-symfony.matt...

Matthias Noback

Thanks for pointing this out. Yes, each specialized event subscriber should have a name describing what it does in particular. There may be events with no subscribers, and some events may have many subscribers.

Matt

Great series Matthias, thanks a lot for writing them, very interesting! One question I have related to db transactions: you say they shouldn't be nested. For example, you mention in a previous article "The first command wasn't fully handled yet. In fact, the database transaction hasn't even been closed yet, so now both commands are being handled within the same transaction. Which is totally undesirable since the second command may fail,
and take the first command down in its fall."

But what if the business need is the other way around? What if one failed transaction should make sure all transactions are rolled back? Just think of any financial/bank application: one Command might do a transfer from account A to B, another Command from B to C, yet another Command from C to D, etc. If any one of them would fail, all of them should be rolled back.

If, as in your example, you let one transaction complete (transfer money from A to B) but then the second transaction fails (B to C), you end up with incorrect amounts of money in the accounts.

Jose Celano

Could you wrap both transfers in the same command?

Jose Celano

Great work Matthias. I am trying to implement a Symfony sample with all this stuff but I have a problem: I do not know how to make the DispatchesEvents CommandBus works. Event handlers are not being called.

More details: https://github.com/josecela...

Matthias Noback

Hi Jose, I'm still finalizing the libraries - next up: looking at your demo application and adding it to the list of sample projects in the documentation. By the way, some other people are making demo applications too!

Cliff

Is there an default implementation of EventProvider class inside SimpleBus?

Matthias Noback

Yes, in version 2.0 it's `RecordsMessages` (https://github.com/SimpleBu...

Cliff

RecordsMessages is an interface not a class. I was wondering if there is a default implementation service (Symsony) which you could inject inside a command handler to dispatch events after the command is done.

Matthias Noback

Ah, sorry about that. I'm thinking about adding one, yes. And now that you ask for it, I will. It is going to be a simple class which implements that interface and uses the `MessageRecorderCapabilities` trait (https://github.com/SimpleBu....

Cliff

Cool! Tnx!

Matthias Noback

There you go: https://github.com/SimpleBu... Documented here: http://simplebus.github.io/... And I will add a message_recorder service to the Symfony bridge as well.

Cliff

Wow thats fast! But why did you not use the MessageRecorderCapabilities trait?

Matthias Noback

Ah! Well, I was trying to, but it already has the protected record() method. I didn't want to make it public, since I think that it should not always be possible to record messages from the outside. So I chose to repeat just this little bit of code. Another option would have been to split the trait of course, but for now it's fine I think.

Kevin Bond

This is great stuff Matthias. Once this series is complete and SimpleBus stable I would love to see a working example app. I am understanding the concepts but am having trouble picturing them all working together in a real world application.

Matthias Noback

Thanks! And I got that request several times :) I will create something to demonstrate everything in a single app.

Mati Beltramone

You already have an example Matthias? Thanks for this series

Matthias Noback

Sorry, no!

Xu Ding

When should we use Command Bus and when to use Application Service?

I assume these two are the approaches in Application layer of Domain Driven Design?

Correct me if I am wrong.

Cheers,
Xu

Matthias Noback

Hi Xu, command handlers may call application services, or they can even replace them.

Xu Ding

Thanks for clarification. Matthias