andreo/eventsauce-messenger
Symfony Messenger integration for EventSauce. Dispatch and consume EventSauce messages via Messenger handlers, with optional autoconfiguration through the AsEventSauceMessageHandler attribute or manual service tagging. Requires PHP 8.2+ and Symfony Messenger 6.2+.
Install Dependencies:
composer require andreo/eventsauce-messenger symfony/messenger spatie/laravel-messenger
spatie/laravel-messenger as a bridge for Symfony Messenger in Laravel.Configure Symfony Messenger:
Add to config/messenger.php:
'buses' => [
'eventBus' => [
'default_middleware' => false,
'middleware' => [
'send_message',
'Andreo\EventSauce\Messenger\Middleware\HandleEventSauceMessageMiddleware',
'handle_message',
],
],
],
Register EventSauce Dispatcher:
In AppServiceProvider:
public function register()
{
$this->app->bind('eventBus.messenger.handlers_locator', function ($app) {
return new \Andreo\EventSauce\Messenger\HandlerLocator(
$app->get('messenger.buses.eventBus.handlers_locator')
);
});
$this->app->bind('eventBus.dispatcher', function ($app) {
return new \Andreo\EventSauce\Messenger\Dispatcher\MessengerMessageDispatcher(
$app->get('messenger.buses.eventBus'),
'eventBus'
);
});
}
Create First Event Handler:
use Andreo\EventSauce\Messenger\Attribute\AsEventSauceMessageHandler;
use EventSauce\EventSourcing\EventConsumption\InflectHandlerMethodsFromType;
class OrderCreatedHandler extends EventConsumer
{
use InflectHandlerMethodsFromType;
#[AsEventSauceMessageHandler(bus: 'eventBus')]
public function onOrderCreated(OrderCreated $event, Message $message): void
{
// Handle event logic
}
}
Dispatch an Event:
use EventSauce\EventSourcing\Message;
$eventBus = $this->app->get('eventBus.dispatcher');
$eventBus->dispatch(new Message(
new OrderCreated('order-123', 'Product A'),
new UniqueMessageId(),
new MessageId(),
new Timestamp()
));
#[AsEventSauceMessageHandler(bus: 'eventBus')]
public function onOrderPaid(OrderPaid $event, Message $message): void
{
Order::query()->where('id', $event->orderId())
->update(['status' => 'paid', 'paid_at' => now()]);
}
#[AsEventSauceMessageHandler(bus: 'eventBus')]
public function onInventoryEvent(InventoryUpdated|InventoryReserved $event, Message $message): void
{
if ($event instanceof InventoryUpdated) {
// Handle update
}
}
# config/messenger.yaml
buses:
eventBus:
middleware:
- 'send_message'
- 'Andreo\EventSauce\Messenger\Middleware\HandleEventSauceMessageMiddleware'
- 'handle_message'
- 'doctrine' # For transactions
- 'laravel_queue' # Spatie's queue middleware
use Andreo\EventSauce\Messenger\Middleware\HandleEventSauceMessageMiddleware;
use Symfony\Component\Messenger\Middleware\RetryFailedMessagesMiddleware;
$middleware = new HandleEventSauceMessageMiddleware(
$handlersLocator,
new RetryFailedMessagesMiddleware(
$bus->getRetryStrategy(),
$bus->getLogger()
)
);
Laravel Service Container: Bind EventSauce components to Laravel’s container:
$this->app->bind('eventsauce.event_store', function ($app) {
return new \EventSauce\EventSourcing\EventStore(
new \EventSauce\EventSourcing\Persistence\DoctrineEventRepository(
$app->make(\Doctrine\DBAL\Connection::class)
)
);
});
Event Serialization: Ensure EventSauce events are JSON-serializable:
class OrderCreated implements \JsonSerializable
{
public function jsonSerialize(): array
{
return [
'order_id' => $this->orderId,
'product' => $this->product,
];
}
}
Testing: Mock the dispatcher in tests:
$dispatcher = $this->app->make('eventBus.dispatcher');
$dispatcher->shouldReceive('dispatch')
->once()
->withArgs(function ($message) {
return $message->message() instanceof OrderCreated;
});
Middleware Order:
HandleEventSauceMessageMiddleware before handle_message in Symfony Messenger’s pipeline to ensure events are processed by EventSauce consumers.Autoconfiguration:
RegisterEventSauceMessageHandlerAttribute::register() is called before Symfony’s autowiring kicks in. In Laravel, this may require a custom Kernel or AppServiceProvider extension.Event Class Naming:
App\Events\OrderCreated). Ensure Laravel’s autoloader includes these paths.Queue Workers:
queue:work must be configured to use the eventBus:
php artisan queue:work --queue=eventBus
Transaction Boundaries:
doctrine middleware for transaction management:
middleware:
- 'doctrine'
- 'Andreo\EventSauce\Messenger\Middleware\HandleEventSauceMessageMiddleware'
Handler Not Found:
handles tag in YAML or the attribute’s bus parameter matches the Symfony Messenger bus alias (eventBus).var/dump($container->has('eventBus.messenger.handlers_locator')) in a Laravel Tinker session.Event Not Dispatched:
MessengerMessageDispatcher is bound to the container and injected correctly:
$dispatcher = $this->app->make('eventBus.dispatcher');
$dispatcher->dispatch($message);
PHP 8.2 Attributes:
Custom HandleMethodInflector: Override the default inflector for custom event-to-method mapping:
use EventSauce\EventSourcing\EventConsumption\HandleMethodInflector;
class CustomInflector implements HandleMethodInflector
{
public function inflect(string $eventClass): string
{
return 'handle' . str_replace('\\', '_', $eventClass);
}
}
Middleware Decorators:
Extend HandleEventSauceMessageMiddleware to add pre/post-processing:
class LoggingMiddleware extends HandleEventSauceMessageMiddleware
{
public function handle(Message $message, callable $next)
{
\Log::info('Processing event', ['event' => $message->message()]);
return parent::handle($message, $next);
}
}
EventSauce Event Store: Replace the default event store with a custom implementation:
$eventStore = new \EventSauce\EventSourcing\EventStore(
new \EventSauce\EventSourcing\Persistence\CustomEventRepository()
);
$this->app->bind('eventsauce.event_store', fn() => $eventStore);
Service Provider Booting:
Ensure the RegisterEventSauceMessageHandlerAttribute is registered after Laravel’s service providers but before Symfony Messenger initializes. Use booted in AppServiceProvider:
protected function boot()
{
if ($this->app->has('kernel')) {
$this->app['kernel']->registerAttribute();
}
}
Queue Middleware:
Spatie’s laravel-messenger may conflict with Symfony’s middleware. Explicitly configure the eventBus to use Laravel’s queue:
buses:
eventBus:
middleware:
- 'send_message'
- 'Andreo\EventSauce\Messenger\Middleware\
How can I help you explore Laravel packages today?