ufo-tech / event-sourcing
A lightweight event sourcing library with aggregates, event store and projections for modern PHP applications.
Installs: 138
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:symfony-bundle
Requires
- php: >=8.4
- ufo-tech/dto-transformer: ^1.2
Requires (Dev)
This package is auto-updated.
Last update: 2025-07-22 14:21:35 UTC
README
A lightweight and modern Event Sourcing library for PHP 8.3+
🚀 Features
- Stores domain events instead of the current state
- Rebuilds aggregate state through event replay
- Supports projections for building read models
- Provides interfaces for custom event stores and dispatchers
📖 Core Concepts
- Resolver — module for generating differences between old and new state.
- Restorer — module for rebuilding an object from changes.
- Merger — service for step-by-step application of changes to an object.
- ContextDTO — DTO for passing context during change analysis (path, collection type, attributes).
- Attributes — PHP 8.3 attributes for controlling change detection behavior.
🛠 Installation
composer require ufo-tech/event-sourcing
📦 Components
Resolver
Class | Description |
---|---|
MainResolver |
Delegates change detection to appropriate resolver |
ObjectResolver |
Handles objects via Reflection API |
CollectionResolver |
Supports associative arrays |
ArrayResolver |
Handles indexed arrays |
ScalarResolver |
Compares scalar values |
Restorer & Merger
Class | Description |
---|---|
Merger |
Merges changes into the base state |
ObjectRestorer |
Rebuilds an object from a collection of changes via DTOTransformer |
ObjectDefinition |
Defines the object type to restore from change history |
Interfaces
Interface | Purpose |
---|---|
ResolverInterface |
Detects changes between states |
MergerInterface |
Merges changes into a state |
RestorerInterface |
Restores an object from change history |
MainResolverInterface |
Encapsulates multiple resolvers |
MainResolverFactoryInterface |
Factory for creating the main resolver |
🟠 Attributes
Attribute | Purpose |
---|---|
#[ChangeIgnore] |
Ignores a field during change detection |
#[AsCollection] |
Marks a field as a key-based collection |
🟢 Contexts
ContextDTO
allows passing additional metadata during resolving:
- Current path (
path
) - Type (collection/array/scalar)
- Special delete placeholder (
__DELETED__
) - Supports nested paths for objects and collections
ContextDTO Rules
ContextDTO::create()
creates a base context with root pathroot
.forPath(string $param)
adds a path segment (e.g.,root.products
).makeAssocByPath(string $path)
marks a path as associative (e.g.,products
,root.products.*
).- Supports pattern paths with
$
that converts to*
for nested collections. - Delete placeholder can be customized via
ContextDTO::create(deletePlaceholder: '__DELETED__')
.
Examples:
use Ufo\EventSourcing\Resolver\ContextDTO; // Single path $context = new ContextDTO(assocPaths: ['products']); $context = new ContextDTO(assocPaths: 'products'); // Multiple paths $context = new ContextDTO(assocPaths: ['products', 'items.subitems']); // Custom delete placeholder $context = new ContextDTO(deletePlaceholder: '__DELETED__', assocPaths: ['products']);
Using patterns:
// All subitems inside any items are treated as collections $context = ContextDTO::create() ->makeAssocByPath('items.$.subitems'); // products.*.discounts.*.details are treated as associative $context = ContextDTO::create() ->makeAssocByPath('products.$.discounts.$.details'); // Root-level collection $context = ContextDTO::create(assocPaths: ['']);
📝 Note: $
automatically converts to *
for wildcard path matching.
📝 Usage Examples
Detecting Changes
$mainResolver = (new DefaultResolverFactory())->create(); $diff = $mainResolver->resolve($oldObject, $newObject);
Using context for associative collection
$context = ContextDTO::create()->makeAssocByPath('products'); $diff = $mainResolver->resolve($old, $new, $context);
Restoring an object from change history
$restorer = new ObjectRestorer(new Merger()); $restored = $restorer->restore( (new ObjectDefinition(MyDto::class)) ->addChanges($firstChange) ->addChanges($secondChange) );
📚 Use Cases
- Event Sourcing and CQRS architectures
- Domain-Driven Design (DDD) based projects
- Service-oriented systems with event-based persistence
📖 Documentation
Full documentation available at docs.ufo-tech.space
📜 License
MIT © UFO Tech