dlakomski/doctrine-orm-bridge
Doctrine ORM bridge for SimpleBus/MessageBus. Provides command bus middlewares to wrap command handling in database transactions and to dispatch domain events generated by Doctrine entities. Links to main SimpleBus repo for issues and PRs.
Installation Add the package via Composer:
composer require simplebus/doctrine-orm-bridge
Register Middleware
Integrate the bridge into your Laravel AppServiceProvider or a dedicated CommandBusServiceProvider:
use SimpleBus\MessageBus\MessageBus;
use SimpleBus\DoctrineORMBridge\Middleware\TransactionMiddleware;
use Doctrine\ORM\EntityManagerInterface;
public function register()
{
$this->app->singleton(MessageBus::class, function ($app) {
$entityManager = $app->make(EntityManagerInterface::class);
$bus = new MessageBus([
new TransactionMiddleware($entityManager),
// Other middleware...
]);
return $bus;
});
}
First Use Case: Transactional Commands Define a command handler that leverages Doctrine transactions:
use SimpleBus\MessageBus\Message\MessageHandlerInterface;
use Doctrine\ORM\EntityManagerInterface;
class CreateUserCommandHandler implements MessageHandlerInterface
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function handle(CreateUserCommand $command)
{
$user = new User();
$user->setName($command->getName());
$this->entityManager->persist($user);
$this->entityManager->flush(); // Transaction auto-commits via middleware
}
}
Middleware Stack
Place TransactionMiddleware first in your middleware stack to ensure transactions wrap the entire command handling:
$bus = new MessageBus([
new TransactionMiddleware($entityManager),
new LoggingMiddleware(),
// Other middleware...
]);
Domain Events Integration
Use DomainEventMiddleware to publish events after command execution:
use SimpleBus\DoctrineORMBridge\Middleware\DomainEventMiddleware;
$bus = new MessageBus([
new TransactionMiddleware($entityManager),
new DomainEventMiddleware($entityManager),
]);
Laravel-Specific Integration
Bind the bridge to Laravel’s container in config/services.php:
'doctrine_orm_bridge' => [
'transaction_middleware' => SimpleBus\DoctrineORMBridge\Middleware\TransactionMiddleware::class,
'domain_event_middleware' => SimpleBus\DoctrineORMBridge\Middleware\DomainEventMiddleware::class,
],
Event Listeners
Attach listeners to Doctrine events (e.g., postPersist) to trigger commands:
$entityManager->getEventManager()->addEventListener(
'postPersist',
new CommandTriggerListener($commandBus, UserCreatedCommand::class)
);
EntityManager State
EntityManager closes. The bridge automatically resets it via ManagerRegistry, but ensure your container rebinds it if needed.EntityManager is singleton-scoped in Laravel’s container.Event Replay
Middleware Order
TransactionMiddleware before DomainEventMiddleware to ensure events are flushed within the same transaction.Transaction Rollbacks
Use Laravel’s DB::transaction() as a sanity check:
DB::transaction(function () use ($bus, $command) {
$bus->dispatch($command);
});
Event Dispatching
Log events before/after DomainEventMiddleware:
$bus = new MessageBus([
new LoggingMiddleware(), // Log before events
new DomainEventMiddleware($entityManager),
new LoggingMiddleware(), // Log after events
]);
Custom Transaction Handling
Extend TransactionMiddleware to add pre/post-transaction logic:
class CustomTransactionMiddleware extends TransactionMiddleware
{
public function handle($message, callable $next)
{
$this->logTransactionStart();
$result = parent::handle($message, $next);
$this->logTransactionEnd();
return $result;
}
}
Event Filtering
Override DomainEventMiddleware to filter events:
class FilteredDomainEventMiddleware extends DomainEventMiddleware
{
protected function shouldProcessEvent($event)
{
return $event instanceof CriticalEvent;
}
}
Laravel Queue Integration Dispatch commands to queues while preserving transactions:
$bus->dispatch(new CreateUserCommand());
// Queue the command via Laravel's queue system
Doctrine Version Compatibility
Ensure your Doctrine ORM version matches the bridge’s requirements (check composer.json).
doctrine/orm:^2.7 for Laravel 8+ compatibility.EntityManager Binding
Laravel’s EntityManager must be bound as a singleton:
$this->app->singleton(EntityManagerInterface::class, function ($app) {
return $app->make('doctrine.entitymanager.entity_manager');
});
How can I help you explore Laravel packages today?