Universal Service Provider for Gember Event Sourcing , compatible with any application or framework that supports container-interop/service-provider.
Install the service provider with composer:
composer require gember/event-sourcing-universal-service-provider
This package installs Gember Event Sourcing with all required dependency adapters. Some of these adapters need to be configured.
By default, it uses the following configuration:
PSR-11 Container definition (https://www.php-fig.org/psr/psr-11):
use Doctrine\DBAL\Connection;
use Gember\IdentityGeneratorSymfony\Ulid\SymfonyUuidIdentityGenerator;
use Psr\Container\ContainerInterface;
use Symfony\Component\Serializer\Serializer;
return [
'gember_event_sourcing' => fn(ContainerInterface $container) => [
'message_bus' => [
'symfony' => [
'event_bus' => $container->get('event.bus'),
],
],
'cache' => [
'enabled' => false,
// set a PSR-6 or PSR-16 implementation
// 'psr16' => $container->get('cache.file'),
// 'psr6' => $container->get('cache.file'),
],
'serializer' => [
'symfony' => [
'serializer' => $container->get(Serializer::class),
],
],
'event_store' => [
'rdbms' => [
'doctrine_dbal' => [
'connection' => $container->get(Connection::class),
],
],
],
'generator' => [
'identity' => [
'service' => $container->get(SymfonyUuidIdentityGenerator::class),
// or use ULIDs
// 'service' => $container->get(SymfonyUlidIdentityGenerator::class),
],
],
'registry' => [
'event_registry' => [
'reflector' => [
'path' => '/path/to/src',
// default: getcwd() . '/../src'
],
],
],
],
];
You can override any of these defaults however you like.
Some of the required dependencies also need to be configured separately.
The default configuration makes use of a service with name event.bus
.
When this bus is configured, Gember Event Sourcing works out of the box.
However, when a different event bus is preferred, it must be a service implementing Symfony\Component\Messenger\MessageBusInterface
.
Example (minimum) configuration for symfony/messenger:
PSR-11 Container definition (https://www.php-fig.org/psr/psr-11):
use Psr\Container\ContainerInterface;
use Symfony\Component\Messenger\Handler\HandlerDescriptor;
use Symfony\Component\Messenger\Handler\HandlersLocator;
use Symfony\Component\Messenger\MessageBus;
use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware;
return [
'event.bus' => fn(ContainerInterface $container) => new MessageBus([
new HandleMessageMiddleware(new HandlersLocator([
// Ideally this could be autoconfigured
SomeDomainEvent::class => [
new HandlerDescriptor([
$container->get(SomeProjector::class),
'onSomeDomainEvent'
]),
],
]), allowNoHandlers: true),
]),
];
The default configuration makes use of a service with name cache.file
.
When this cache service is configured, Gember Event Sourcing works out of the box.
However, when a different cache pool is preferred, it must be a service implementing Psr\Cache\CacheItemPoolInterface
(PSR-6) or Psr\SimpleCache\CacheInterface
(PSR-16).
Example (minimum) configuration for symfony/cache:
PSR-11 Container definition (https://www.php-fig.org/psr/psr-11):
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Psr16Cache;
return [
'cache.file' => fn() => new Psr16Cache(new FilesystemAdapter(
directory: __DIR__ . '/../../var/cache',
)),
];
The default configuration makes use of a service with name Symfony\Component\Serializer\Serializer
.
When this serializer service is configured, Gember Event Sourcing works out of the box.
However, when a different serializer is preferred, it must be a service implementing Symfony\Component\Serializer\SerializerInterface
.
Example (minimum) configuration for symfony/serializer:
PSR-11 Container definition (https://www.php-fig.org/psr/psr-11):
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
return [
Serializer::class => fn() => new Serializer([
new DateTimeNormalizer([
DateTimeNormalizer::FORMAT_KEY => 'Y-m-d\TH:i:s.uP',
]),
new ObjectNormalizer(),
], [
new JsonEncoder(),
]),
];
The default configuration makes use of a service with name Doctrine\DBAL\Connection
.
When this connection service is configured, Gember Event Sourcing works out of the box.
However, when a different Doctrine connection is preferred, it must be a service implementing Doctrine\DBAL\Connection
.
Example (minimum) configuration for doctrine/dbal:
PSR-11 Container definition (https://www.php-fig.org/psr/psr-11):
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
return [
Connection::class => fn() => DriverManager::getConnection([
'host' => $_ENV['DB_HOST'],
'port' => $_ENV['DB_PORT'],
'dbname' => $_ENV['DB_DBNAME'],
'user' => $_ENV['DB_USER'],
'password' => $_ENV['DB_PASSWORD'],
'driver' => $_ENV['DB_DRIVER'],
'charset' => $_ENV['DB_CHARSET'],
]),
];
In order to persist all domain events in database, a running SQL database is needed. The event store requires two tables. Schema is available in either raw SQL or in a migration file format:
Raw SQL schema: https://github.com/GemberPHP/rdbms-event-store-doctrine-dbal/blob/main/resources/schema.sql
Migrations:
- Doctrine migrations: https://github.com/GemberPHP/rdbms-event-store-doctrine-dbal/blob/main/resources/migrations/doctrine
- Phinx migrations: https://github.com/GemberPHP/rdbms-event-store-doctrine-dbal/tree/main/resources/migrations/phinx
Check the main package gember/event-sourcing or the demo application gember/example-event-sourcing-dcb for examples.