dbellettini/eventstore-client-bundle
Install the Package
composer require dbellettini/eventstore-client-bundle
Ensure symfony/flex is configured to auto-load the bundle in config/bundles.php.
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'
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
);
}
}
Verify Connection Use TTL (Time-To-Live) or a health check endpoint to confirm the connection:
$this->eventStoreClient->connect();
Event Publishing
appendToStream() for writing events to a stream.OrderCreated, PaymentProcessed).eventId and streamId (e.g., user-{id} or order-{id}).Event Subscribing
subscribeToStream() or subscribeToAll().$subscription = $this->eventStoreClient->subscribeToStream(
streamId: 'order-123',
fromRevision: 0
);
$subscription->onEvent(function (EventEnvelope $event) {
// Handle event (e.g., update UI, trigger side effects)
});
Reading Events
readStreamForward() or readAllForward().$events = $this->eventStoreClient->readStreamForward(
streamId: 'order-123',
fromRevision: 0,
count: 10
);
Error Handling
EventStoreClientException (connection issues).WrongExpectedVersionException (optimistic concurrency conflicts).try {
$this->eventStoreClient->appendToStream(...);
} catch (WrongExpectedVersionException $e) {
// Retry with updated expectedVersion or notify admin
}
config/app.php:
'bindings' => [
Dbellettini\EventStoreClientBundle\Client\EventStoreClientInterface::class
=> \Illuminate\Contracts\Foundation\Application::class,
],
EventSerializerInterface and configuring it in config/packages/eventstore_client.yaml:
eventstore_client:
serializer: App\EventStore\CustomEventSerializer
EventStoreClientInterface in PHPUnit tests:
$this->mock(EventStoreClientInterface::class)
->shouldReceive('appendToStream')
->once();
Connection Management
if (!$this->eventStoreClient->isConnected()) {
$this->eventStoreClient->connect();
}
Stream Naming
orders) can lead to performance bottlenecks or conflicts.user-123-orders) and leverage projections for querying.Event Versioning
WrongExpectedVersionException if the stream’s current version doesn’t match expectedVersion.ExpectedVersion::Any for initial writes or implement a retry with ExpectedVersion::StreamExists.Serialization Quirks
EventSerializer or use JMS Serializer:
eventstore_client:
serializer: jms_serializer.event_store_serializer
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.
Custom Middleware Add middleware to the client for cross-cutting concerns (e.g., logging, metrics):
$client->addMiddleware(new LoggingMiddleware());
Event Metadata Attach metadata to events for debugging or auditing:
$event->setMetadata(['user-agent' => 'laravel-app', 'timestamp' => now()->toIso8601String()]);
Projection Management Leverage EventStoreDB projections for querying events without loading them into your app:
$this->eventStoreClient->executeCommand(
command: 'CREATE CONTINUOUS PROJECTION "OrderProjection" FROM ALL'
);
How can I help you explore Laravel packages today?