domainflow / event-sourcing-redis
EventSourcing Redis adapter package for DomainFlow EventSourcing
Package info
github.com/domainflow/event-sourcing-redis
pkg:composer/domainflow/event-sourcing-redis
Requires
- php: ^8.4
- ext-redis: *
- domainflow/event-sourcing-core: ^v0.1.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.70
- mikey179/vfsstream: ^1.6
- nikic/php-parser: ^5.4
- phpstan/phpstan: ^2.1
- phpstan/phpstan-deprecation-rules: ^2.0
- phpunit/phpunit: ^12.5
- symfony/phpunit-bridge: ^7.2
README
A Redis storage adapter for domainflow/event-sourcing-core
— implements EventStorageInterface, SnapshotStorageInterface, SnapshotHistoryStorageInterface, and ProcessManagerStorageInterface against Redis. No domain logic of its own — no aggregate modeling, no business rules, just translation between Core's interfaces and Redis.
Requirements
- PHP 8.4+
ext-redis- A reachable Redis 7+ instance, configured as below
Installation
composer require domainflow/event-sourcing-redis
Production requirements
An event store is a system of record, and Redis' defaults are not built for one. Two settings are mandatory, and RedisEventStorage refuses to start without them rather than let you find out later:
appendonly yes
appendfsync always # everysec is acceptable if losing up to a second of events is
maxmemory-policy noeviction
appendonly yes. With RDB snapshotting alone, a crash loses every event written since the last snapshot.appendfsync alwaysis what "no event is ever lost" actually costs;everysectrades a one-second window for throughput.maxmemory-policy noeviction. Anyallkeys-*policy lets Redis discard event streams to reclaim memory. There is no error and no log line — an aggregate simply replays to a state it was never in. This is the realistic failure, because it is what happens when the adapter is pointed at an instance that is already serving as a cache.
The check runs once, when the storage is constructed. If your host forbids CONFIG GET — several managed Redis offerings do — verify the settings yourself and pass assertDurableConfiguration: false:
$storage = new RedisEventStorage($redis, assertDurableConfiguration: false);
storeEvents() is all-or-nothing for the whole call. One Lua script appends every event of the call, across every aggregate it touches, and verifies all their versions before writing any of them. If it throws, nothing was written and the identical batch can be retried.
Redis Cluster is not supported. That script touches every aggregate's stream plus the global index in one atomic call, and those keys cannot share a hash slot unless every key in the store shares one — at which point the cluster is a single node with extra steps. Use a single instance, or Sentinel for failover.
Development
docker compose up -d # start a local Redis instance composer install composer quality # lint + static analysis + full test suite (100% coverage required) + audit
License
MIT — see LICENSE.