Install the Bundle
composer require andreo/eventsauce-bundle
Enable in config/bundles.php:
Andreo\EventSauceBundle\AndreoEventSauceBundle::class => ['all' => true],
Configure Doctrine Event Store
Add to config/packages/andreo_event_sauce.yaml:
andreo_event_sauce:
message_storage:
repository:
doctrine_3:
enabled: true
table_name: event_store
First Use Case: Event Sourcing
Define an aggregate root (e.g., FooAggregate) and events (e.g., FooCreated). Use the EventSauce\EventSourcing\AggregateRoot trait:
use EventSauce\EventSourcing\AggregateRoot;
final class FooAggregate extends AggregateRoot
{
public function create(string $name): void
{
$this->recordThat(new FooCreated($name));
}
}
Persist the aggregate:
$aggregate = new FooAggregate();
$aggregate->create('Test');
$repository->persist($aggregate);
Event-Driven Projections
Use EventConsumer to react to events (e.g., update read models):
#[AsSyncMessageConsumer(dispatcher: 'foo_dispatcher')]
final class FooProjectionConsumer extends EventConsumer
{
use InjectedHandleMethodInflector;
public function onFooCreated(FooCreated $event, Message $message): void
{
// Update read model
}
}
Anti-Corruption Layer (ACL) Translate external events to domain events:
#[AsMessageTranslator]
final readonly class ExternalToDomainTranslator implements MessageTranslator
{
public function translateMessage(Message $message): Message
{
return new Message(new DomainEvent($message->payload()));
}
}
Messenger Integration Dispatch events via Symfony Messenger:
andreo_event_sauce:
message_dispatcher:
foo_dispatcher:
type:
messenger:
bus: event_bus
Configure the bus in framework/messenger:
buses:
event_bus:
middleware:
- event_bus.handle_eventsauce_message
Snapshotting Enable snapshots for performance:
andreo_event_sauce:
snapshot:
enabled: true
repository:
doctrine:
enabled: true
table_name: snapshot_store
Annotate aggregates:
#[Snapshot]
final class FooAggregate extends AggregateRoot { ... }
Upcasting Handle versioned events:
#[AsUpcaster(aggregateClass: FooAggregate::class, version: 2)]
final class FooEventV2Upcaster implements Upcaster
{
public function upcast(array $message): array { ... }
}
php bin/console doctrine:migrations:diff after defining aggregates.EventSauce\EventSourcing\Testing\AggregateRootMother for fixtures.config/packages/dev/andreo_event_sauce.yaml:
debug: true
Message Dispatcher Order
messenger dispatchers are placed after send_message middleware in Symfony Messenger config.ACL Owners Misconfiguration
MessageTranslator or MessageFilter is tagged with incorrect owners, it won’t apply to intended consumers/dispatchers.tags:
name: andreo.eventsauce.acl.message_translator
owners: [App\Consumer\FooConsumer]
Snapshot Versioning
versioned: true in config and proper SnapshotVersionInflector setup.snapshot_store table.Upcasting Timing
before_unserialize upcasters modify raw event data; after_unserialize (via eventsauce-upcasting) work on hydrated objects.EventSauce\EventSourcing\Testing\AggregateRootMother::withEvents().Outbox Relay ID
relay_id in message_outbox must match the command used to consume messages:
bin/console andreo:eventsauce:message-outbox:consume event_dispatcher_relay
Event Not Fired?
Check if the aggregate’s recordThat() is called and the event is serialized. Enable debug logs:
andreo_event_sauce:
debug: true
Consumer Not Triggered?
Verify the consumer is tagged with andreo.eventsauce.sync_message_consumer or annotated with #[AsSyncMessageConsumer].
Test with:
bin/console debug:container App\Consumer\FooConsumer
ACL Not Applied?
Ensure the translator/filter is registered with the correct owners and trigger (e.g., before_translate).
Custom Message Decorators
Extend EventSauce\EventSourcing\MessageDecorator to add metadata (e.g., tenant IDs):
#[AsMessageDecorator]
final class TenantDecorator implements MessageDecorator
{
public function decorate(Message $message): Message
{
return new Message($message->payload(), $message->metadata() + ['tenant_id' => '123']);
}
}
Dynamic Dispatchers
Use Symfony’s autoconfigure: true to auto-register dispatchers via attributes:
services:
_defaults:
autoconfigure: true
Custom Upcasters
Implement EventSauce\EventSourcing\Upcasting\Upcaster for aggregate-specific logic:
#[AsUpcaster(aggregateClass: OrderAggregate::class, version: 3)]
final class OrderV3Upcaster implements Upcaster { ... }
Event Outbox Relays
Extend EventSauce\MessageOutbox\Relay to support custom transport (e.g., Kafka):
use EventSauce\MessageOutbox\Relay;
final class KafkaRelay implements Relay { ... }
andreo_event_sauce.clock.timezone matches your app’s timezone to avoid event timestamp issues.connection in doctrine_3 config if using multiple DBs:
connection: doctrine.dbal.secondary_connection
bus key in messenger dispatchers must match Symfony’s framework.messenger.buses config.How can I help you explore Laravel packages today?