david2m/prooph-event-store-doctrine-adapter-bundle
Install the Bundle
composer require david2m/prooph-event-store-doctrine-adapter-bundle
Register the bundle in config/bundles.php:
return [
// ...
David2M\ProophEventStoreDoctrineAdapterBundle\ProophEventStoreDoctrineAdapterBundle::class => ['all' => true],
];
Configure Doctrine & Event Store
Update config/packages/prooph_event_store.yaml (if missing, create it):
prooph_event_store:
event_store:
doctrine_event_store:
connection: default
event_class: 'Prooph\EventStore\Aggregate\AggregateChanged'
metadata_class: 'Prooph\EventStore\Aggregate\AggregateMetadata'
repository_class: 'Prooph\EventStore\Aggregate\AggregateRepository'
stream_name_prefix: 'prooph_'
First Use Case: Publishing an Event
use Prooph\EventStore\EventStore;
use Prooph\EventStore\Aggregate\AggregateChanged;
// Inject EventStore via Symfony's DI container
$eventStore = $this->container->get(EventStore::class);
// Publish an event
$event = new AggregateChanged(
'user-created',
['user_id' => '123', 'name' => 'John Doe'],
new \DateTimeImmutable()
);
$eventStore->dispatch($event);
Aggregate Root Management
Use AggregateRepository to load and persist aggregates:
$repository = $this->container->get('prooph_event_store.aggregate_repository');
$aggregate = $repository->getAggregateRoot('user-123');
$aggregate->recordThat(new UserRegistered('John Doe'));
$repository->saveAggregateRoot($aggregate);
Event Sourcing with Doctrine
Query events via Doctrine’s EntityManager:
$events = $entityManager->getRepository(AggregateChanged::class)
->findBy(['stream_name' => 'user-123']);
Symfony Command Integration Process events in background jobs:
use Symfony\Component\Console\Command\Command;
use Prooph\EventStore\EventStore;
class ProcessEventsCommand extends Command {
protected static $defaultName = 'app:process-events';
public function __construct(private EventStore $eventStore) {}
protected function execute(InputInterface $input, OutputInterface $output): int {
$this->eventStore->dispatch(new EventToProcess());
return Command::SUCCESS;
}
}
make:migration to create tables for aggregate_changed and aggregate_metadata.config/services.yaml:
services:
App\EventListener\MyEventListener:
tags:
- { name: 'prooph_event_store.event_listener' }
prooph/service-bus for command/query separation.Stream Naming Collisions
Ensure stream_name_prefix in config is unique to avoid conflicts with other bundles.
Doctrine Connection Issues
Verify connection: default in config matches your Doctrine setup. Use connection: custom if needed.
Event Serialization
Custom event classes must implement JsonSerializable or use Prooph’s SerializableAggregateRoot.
Event Not Persisted?
Check Doctrine’s event_store.aggregate_changed table for records. Enable Prooph’s debug mode:
prooph_event_store:
debug: true
Metadata Missing?
Ensure metadata_class in config matches AggregateMetadata (default: Prooph\EventStore\Aggregate\AggregateMetadata).
Custom Event Store
Override the default DoctrineEventStore by binding a custom implementation in services.yaml:
services:
Prooph\EventStore\EventStore: '@my_custom_event_store'
Event Store Middleware Add middleware to the event store pipeline:
$eventStore->addMiddleware(new MyCustomMiddleware());
Doctrine Event Listeners Extend Prooph’s event listeners for custom logic:
class CustomEventListener implements EventListener {
public function __invoke(Recorded $recorded): void {
// Custom logic here
}
}
How can I help you explore Laravel packages today?