DoctrineORMBridge

This package provides command bus middlewares that can be used to integrate SimpleBus/MessageBus with Doctrine ORM.

It provides an easy way to wrap command handling in a database transaction and handle domain events generated by entities.

@TODO The intro should explain what it does.

Getting started

Installation

Using Composer:

1
composer require simple-bus/doctrine-orm-bridge

Preparations

To use the middlewares provided by the library, set up a command bus and an event bus, if you didn’t already do this:

1
2
3
4
5
6
7
use SimpleBus\Message\Bus\Middleware\MessageBusSupportingMiddleware;

$commandBus = new MessageBusSupportingMiddleware();
...

$eventBus = new MessageBusSupportingMiddleware();
...

Make sure to also properly set up an entity manager:

1
2
// $entityManager is an instance of Doctrine\ORM\EntityManager
$entityManager = ...;

Now add the available middlewares for transaction handling and domain events.

Transactions

It is generally a good idea to wrap command handling in a database transaction. If you want to do this, add the WrapsMessageHandlingInTransaction middleware to the command bus. Provide an instance of the Doctrine ManagerRegistry interface and the name of the entity manager that you want to use.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
use SimpleBus\DoctrineORMBridge\MessageBus\WrapsMessageHandlingInTransaction;

/*
 * $managerRegistry is an instance of Doctrine\Common\Persistence\ManagerRegistry
 *
 * For example: if you use Symfony, use the "doctrine" service
 */
$managerRegistry = ...;

$transactionalMiddleware = new WrapsMessageHandlingInTransaction($managerRegistry, 'default');

$commandBus->addMiddleware($transactionalMiddleware);

Note

Once you have added this middleware, you shouldn’t call EntityManager::flush() manually from inside your command handlers anymore.

Domain events

Using the message recorder facilities from SimpleBus/MessageBus you can let Doctrine ORM collect domain events and subsequently let the event bus handle them.

Make sure that your entities implement the ContainsRecordedMessages interface. Use the PrivateMessageRecorderCapabilities trait to conveniently record events from inside the entity:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
use SimpleBus\Message\Recorder\ContainsRecordedMessages;
use SimpleBus\Message\Recorder\PrivateMessageRecorderCapabilities;

class YourEntity implements ContainsRecordedMessages
{
    use PrivateMessageRecorderCapabilities;

    public function changeSomething()
    {
        // record new events like this:

        $this->record(new SomethingChanged());
    }
}

Then set up the event recorder for Doctrine entities:

1
2
3
4
5
use SimpleBus\DoctrineORMBridge\EventListener\CollectsEventsFromEntities;

$eventRecorder = new CollectsEventsFromEntities();

$entityManager->getConnection()->getEventManager()->addEventSubscriber($eventRecorder);

The event recorder will loop over all the entities that were involved in the last database transaction and collect their internally recorded events.

After a database transaction was completed successfully these events should be handled by the event bus. This is done by a specialized middleware, which should be appended to the command bus before the middleware that is responsible for handling the transaction.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
use SimpleBus\DoctrineORMBridge\MessageBus\WrapsMessageHandlingInTransaction;

use SimpleBus\Message\Bus\MessageBus;

$eventDispatchingMiddleware = new HandlesRecordedMessagesMiddleware($eventProvider, $eventBus);
// N.B. append this middleware *before* the WrapsMessageHandlingInTransaction middleware
$commandBus->appendMiddleware($eventDispatchingMiddleware);

$transactionalMiddleware = new WrapsMessageHandlingInTransaction($entityManager);
$commandBus->appendMiddleware($transactionalMiddleware);

Note

The MessageBusSupportingMiddleware class also has a prependMiddleware() method, which you can use to prepend middleware instead of appending it.