gember/event-sourcing-universal-service-provider

Universal service provider for Gember Event Sourcing (gember/event-sourcing)

Maintainers

Package info

github.com/GemberPHP/event-sourcing-universal-service-provider

pkg:composer/gember/event-sourcing-universal-service-provider

Transparency log

Statistics

Installs: 5

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

0.11.0 2026-08-24 19:17 UTC

This package is auto-updated.

Last update: 2026-08-24 19:18:08 UTC


README

Software License PHP Version

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:

Good to go!

Check the main package gember/event-sourcing or the demo application gember/example-event-sourcing-dcb for examples.