Weave 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

Eventstore Client Bundle Laravel Package

dbellettini/eventstore-client-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Package

    composer require dbellettini/eventstore-client-bundle
    

    Ensure symfony/flex is configured to auto-load the bundle in config/bundles.php.

  2. Configure the Bundle Add EventStore connection details to config/packages/eventstore_client.yaml:

    eventstore_client:
        connections:
            default:
                host: 'localhost'
                port: 2113
                scheme: 'tcp'
                username: 'admin'
                password: 'changeit'
    
  3. First Use Case: Publishing an Event Inject the EventStoreClient service and publish an event:

    use Dbellettini\EventStoreClientBundle\Client\EventStoreClientInterface;
    
    class OrderService
    {
        public function __construct(
            private EventStoreClientInterface $eventStoreClient
        ) {}
    
        public function createOrder(): void
        {
            $event = new OrderCreated(
                uuid: Uuid::uuid4(),
                userId: 'user-123',
                amount: 100.00
            );
    
            $this->eventStoreClient->appendToStream(
                streamId: 'order-123',
                events: [$event],
                expectedVersion: ExpectedVersion::NoStream
            );
        }
    }
    
  4. Verify Connection Use TTL (Time-To-Live) or a health check endpoint to confirm the connection:

    $this->eventStoreClient->connect();
    

Implementation Patterns

Core Workflows

  1. Event Publishing

    • Use appendToStream() for writing events to a stream.
    • Example: Domain events (e.g., OrderCreated, PaymentProcessed).
    • Pattern: Always include a unique eventId and streamId (e.g., user-{id} or order-{id}).
  2. Event Subscribing

    • Subscribe to streams or all events using subscribeToStream() or subscribeToAll().
    • Example: Real-time updates for dashboards or notifications.
    $subscription = $this->eventStoreClient->subscribeToStream(
        streamId: 'order-123',
        fromRevision: 0
    );
    
    $subscription->onEvent(function (EventEnvelope $event) {
        // Handle event (e.g., update UI, trigger side effects)
    });
    
  3. Reading Events

    • Fetch events by stream with readStreamForward() or readAllForward().
    • Example: Reconstructing state from historical events (CQRS).
    $events = $this->eventStoreClient->readStreamForward(
        streamId: 'order-123',
        fromRevision: 0,
        count: 10
    );
    
  4. Error Handling

    • Wrap EventStore operations in try-catch blocks to handle:
      • EventStoreClientException (connection issues).
      • WrongExpectedVersionException (optimistic concurrency conflicts).
    try {
        $this->eventStoreClient->appendToStream(...);
    } catch (WrongExpectedVersionException $e) {
        // Retry with updated expectedVersion or notify admin
    }
    

Integration Tips

  • Laravel-Specific: Use Symfony’s DI container to bind the bundle’s services to Laravel’s container in config/app.php:
    'bindings' => [
        Dbellettini\EventStoreClientBundle\Client\EventStoreClientInterface::class
            => \Illuminate\Contracts\Foundation\Application::class,
    ],
    
  • Event Serialization: Customize serialization by implementing EventSerializerInterface and configuring it in config/packages/eventstore_client.yaml:
    eventstore_client:
        serializer: App\EventStore\CustomEventSerializer
    
  • Testing: Mock the EventStoreClientInterface in PHPUnit tests:
    $this->mock(EventStoreClientInterface::class)
         ->shouldReceive('appendToStream')
         ->once();
    

Gotchas and Tips

Pitfalls

  1. Connection Management

    • Issue: The bundle does not auto-reconnect on failure. Silent failures may occur if the connection drops.
    • Fix: Implement a retry mechanism or health checks:
      if (!$this->eventStoreClient->isConnected()) {
          $this->eventStoreClient->connect();
      }
      
  2. Stream Naming

    • Issue: Using generic stream names (e.g., orders) can lead to performance bottlenecks or conflicts.
    • Fix: Use scoped stream names (e.g., user-123-orders) and leverage projections for querying.
  3. Event Versioning

    • Issue: WrongExpectedVersionException if the stream’s current version doesn’t match expectedVersion.
    • Fix: Use ExpectedVersion::Any for initial writes or implement a retry with ExpectedVersion::StreamExists.
  4. Serialization Quirks

    • Issue: Default JSON serialization may fail for complex objects (e.g., DateTime, Uuid).
    • Fix: Extend EventSerializer or use JMS Serializer:
      eventstore_client:
          serializer: jms_serializer.event_store_serializer
      

Debugging

  • Enable Logging: Configure Monolog to log EventStore interactions:

    monolog:
        handlers:
            eventstore:
                type: stream
                handler: monolog.handler.console
                channels: ["eventstore"]
    

    Then log events in your service:

    $this->logger->info('Appending event', ['event' => $event]);
    
  • Check EventStore UI: Use the EventStoreDB UI to inspect streams and events during development.

Extension Points

  1. Custom Middleware Add middleware to the client for cross-cutting concerns (e.g., logging, metrics):

    $client->addMiddleware(new LoggingMiddleware());
    
  2. Event Metadata Attach metadata to events for debugging or auditing:

    $event->setMetadata(['user-agent' => 'laravel-app', 'timestamp' => now()->toIso8601String()]);
    
  3. Projection Management Leverage EventStoreDB projections for querying events without loading them into your app:

    $this->eventStoreClient->executeCommand(
        command: 'CREATE CONTINUOUS PROJECTION "OrderProjection" FROM ALL'
    );
    
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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log