Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Event Store Doctrine Adapter Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install Dependencies

    composer require prooph/event-store-doctrine-adapter prooph/event-store
    
  2. 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'),
    ],
    
  3. 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);
    
  4. 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)
    );
    

Implementation Patterns

Core Workflows

  1. Event Sourcing with Doctrine

    • Use DoctrineEventStoreAdapter as a drop-in replacement for Prooph’s default adapters.
    • Leverage Doctrine’s connection pooling and transactions for atomicity:
      $connection->beginTransaction();
      try {
          $eventStore->appendToStream($streamName, $events);
          $connection->commit();
      } catch (\Exception $e) {
          $connection->rollBack();
          throw $e;
      }
      
  2. Stream Loading

    • Load events by stream name or UUID:
      $events = $eventStore->load($streamName, 0, 10); // Load first 10 events
      $events = $eventStore->loadByUuid($streamUuid);
      
  3. Aggregate Reconstruction

    • Rebuild aggregates from loaded events:
      $aggregate = AggregateRoot::fromStreamName(
          $streamName,
          AggregateType::fromAggregateRoot($this),
          $eventStore->load($streamName)
      );
      

Integration Tips

  • 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);
    

Gotchas and Tips

Pitfalls

  1. Transaction Isolation

    • Ensure your RDBMS supports REPEATABLE READ or higher isolation levels to avoid phantom reads during concurrent appends.
    • Laravel’s default transaction isolation may not suffice; configure explicitly:
      $connection->setTransactionIsolation(\PDO::TRANSACTION_REPEATABLE_READ);
      
  2. Schema Migrations

    • The adapter’s default schema assumes a prooph_event table. If you customize the schema, manually migrate existing data or handle schema changes carefully.
  3. Event Ordering

    • Doctrine DBAL does not enforce event ordering by default. Rely on Prooph’s internal ordering logic or add a position column to your schema.
  4. Performance with Large Streams

    • Loading entire streams can be slow. Use pagination (load($streamName, $fromSequence, $limit)) or lazy-load events.

Debugging

  • 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.

Extension Points

  1. Custom Serialization Override Prooph\EventStore\Serializer\Serializer to handle non-JSON events:

    $serializer = new CustomSerializer();
    $eventStore->setSerializer($serializer);
    
  2. 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',
            ];
        }
    };
    
  3. Event Store Decorators Use Prooph’s decorator pattern to add cross-cutting concerns (e.g., logging, auditing):

    $eventStore = new LoggingEventStoreDecorator($eventStore);
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager