gember / event-sourcing-universal-service-provider
Universal service provider for Gember Event Sourcing (gember/event-sourcing)
Package info
github.com/GemberPHP/event-sourcing-universal-service-provider
pkg:composer/gember/event-sourcing-universal-service-provider
Requires
- php: ^8.3
- container-interop/service-provider: ^0.4.1
- doctrine/dbal: ^4.2
- gember/dependency-contracts: ^0.7
- gember/event-sourcing: ^0.18
- gember/identity-generator-symfony: ^0.12
- gember/message-bus-symfony: ^0.12
- gember/rdbms-event-store-doctrine-dbal: ^0.16
- gember/serializer-symfony: ^0.12
- psr/container: ^2.0
- psr/log: ^3.0
- psr/simple-cache: ^3.0
- symfony/cache: ^7.1|^7.2|^8.0
- symfony/property-access: ^7.1|^7.2|^8.0
- symfony/serializer: ^7.1|^7.2|^8.0
- symfony/uid: ^7.1|^7.2|^8.0
Requires (Dev)
- captainhook/captainhook: ^5.23
- friendsofphp/php-cs-fixer: ^3.64
- phpstan/phpstan: ^2.1
- rector/rector: ^2.0
- shipmonk/composer-dependency-analyser: ^1.7
README
Universal Service Provider for Gember Event Sourcing , compatible with any application or framework that supports container-interop/service-provider.
Installation
Install the service provider with composer:
composer require gember/event-sourcing-universal-service-provider
Configuration
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'), 'command_bus' => $container->get('command.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' => [ 'reflector' => [ 'path' => '/path/to/src', // default: getcwd() . '/../src' ], ], 'command_handler' => [ 'reflector' => [ 'path' => '/path/to/src', // default: getcwd() . '/../src' ], ], 'saga' => [ 'reflector' => [ 'path' => '/path/to/src', // default: getcwd() . '/../src' ], ], ], 'logging' => [ // 'logger' => $container->get(LoggerInterface::class), ], 'snapshot' => [ 'enabled' => false, ], 'dispatch' => [ 'strategy' => 'direct', // 'direct' or 'outbox' 'max_retries' => 5, // only used with outbox strategy ], ], ];
You can override any of these defaults however you like.
Required dependencies
Some of the required dependencies also need to be configured separately.
Symfony Messenger (symfony/messenger)
The default configuration makes use of services with name event.bus and command.bus.
When these buses are configured, Gember Event Sourcing works out of the box.
However, when different buses are preferred, they must be services 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), ]), 'command.bus' => fn(ContainerInterface $container) => new MessageBus([ new HandleMessageMiddleware(new HandlersLocator([ SomeCommand::class => [ new HandlerDescriptor([ $container->get(SomeCommandHandler::class), '__invoke' ]), ], ])), ]), ];
Symfony Cache (symfony/cache)
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', )), ];
Symfony Serializer (symfony/serializer)
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(), ]), ];
Doctrine DBAL/ORM (doctrine/dbal, doctrine/orm)
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'], ]), ];
Database
In order to persist domain events, sagas, snapshots, and outbox messages, a running SQL database is needed. 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
Good to go!
Check the main package gember/event-sourcing or the demo application gember/example-event-sourcing-dcb for examples.