prooph/event-store-doctrine-adapter
Doctrine DBAL adapter for Prooph Event Store. Stores and loads event streams using Doctrine connections, with support for transactional appends and stream persistence in relational databases. Useful for event-sourced PHP/Laravel apps needing DB-backed storage.
Install Dependencies
composer require prooph/event-store-doctrine-adapter prooph/event-store
Configure Doctrine DBAL Connection
Ensure your Laravel config/database.php includes a connection (e.g., event_store) with the target RDBMS. Example:
'event_store' => [
'driver' => 'pdo_mysql',
'host' => env('DB_HOST'),
'database' => env('DB_DATABASE'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
],
Initialize the Adapter
use Prooph\EventStore\Adapter\Doctrine\DoctrineEventStoreAdapter;
use Doctrine\DBAL\DriverManager;
$connection = DriverManager::getConnection([
'url' => env('DATABASE_URL').'?serverVersion='.env('DB_CONNECTION').'_version',
]);
$eventStore = new DoctrineEventStoreAdapter($connection);
First Use Case: Appending an Event
use Prooph\EventStore\Aggregate\AggregateRoot;
use Prooph\EventStore\Aggregate\AggregateType;
$aggregate = new AggregateRoot(AggregateType::fromAggregateRoot($this));
$aggregate->recordThat(new \App\Events\UserRegistered('user@example.com'));
$eventStore->appendToStream(
'user-123',
AggregateRoot::getRecordedEvents($aggregate)
);
Event Sourcing with Doctrine
DoctrineEventStoreAdapter as a drop-in replacement for Prooph’s default adapters.$connection->beginTransaction();
try {
$eventStore->appendToStream($streamName, $events);
$connection->commit();
} catch (\Exception $e) {
$connection->rollBack();
throw $e;
}
Stream Loading
$events = $eventStore->load($streamName, 0, 10); // Load first 10 events
$events = $eventStore->loadByUuid($streamUuid);
Aggregate Reconstruction
$aggregate = AggregateRoot::fromStreamName(
$streamName,
AggregateType::fromAggregateRoot($this),
$eventStore->load($streamName)
);
Laravel Service Providers
Bind the adapter in AppServiceProvider:
$this->app->singleton(DoctrineEventStoreAdapter::class, function ($app) {
return new DoctrineEventStoreAdapter(
$app['db']->connection('event_store')->getDoctrineConnection()
);
});
Event Serialization
Use Prooph’s default JSON serializer or extend Prooph\EventStore\Serializer\Serializer for custom formats.
Schema Management
The adapter auto-creates tables if they don’t exist. For custom schemas, extend Prooph\EventStore\Adapter\Doctrine\Schema\DefaultSchema:
$schema = new CustomSchema();
$eventStore->setSchema($schema);
Transaction Isolation
REPEATABLE READ or higher isolation levels to avoid phantom reads during concurrent appends.$connection->setTransactionIsolation(\PDO::TRANSACTION_REPEATABLE_READ);
Schema Migrations
prooph_event table. If you customize the schema, manually migrate existing data or handle schema changes carefully.Event Ordering
position column to your schema.Performance with Large Streams
load($streamName, $fromSequence, $limit)) or lazy-load events.Enable Doctrine Logging
$connection->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
To inspect raw SQL queries.
Check for Deadlocks
Monitor Doctrine’s SQLSTATE[40001] errors (deadlocks) and retry failed transactions.
Custom Serialization
Override Prooph\EventStore\Serializer\Serializer to handle non-JSON events:
$serializer = new CustomSerializer();
$eventStore->setSerializer($serializer);
Event Metadata
Extend the metadata column in the schema to store additional data (e.g., timestamps, user IDs):
$schema = new class extends DefaultSchema {
protected function getMetadataTableColumns() {
return [
'id' => 'INT AUTO_INCREMENT PRIMARY KEY',
'stream_name' => 'VARCHAR(255)',
'event_name' => 'VARCHAR(255)',
'data' => 'TEXT',
'metadata' => 'JSON', // Custom column
'created_at' => 'TIMESTAMP',
];
}
};
Event Store Decorators Use Prooph’s decorator pattern to add cross-cutting concerns (e.g., logging, auditing):
$eventStore = new LoggingEventStoreDecorator($eventStore);
How can I help you explore Laravel packages today?