spriebsch / domain-event
Domain Events, Done Right.
3.0.0
2026-10-17 07:44 UTC
Requires
- php: >=8.5
- spriebsch/filesystem: ^1.0
- spriebsch/timestamp: ^1.1
- spriebsch/uuid: ^2.1
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Domain Events, Done Right.
Features
- Structured Topics: Define topics using a
vendor.domain.context.nameformat. - Event Enveloping: Wrap domain events in an
Envelopethat carries essential metadata:EventId(UUIDv4) to uniquely identify the eventTimestamp(Received and Persisted)Topic- optional
CausationIdandCorrelationIdfor tracing - an optional
SchemaVersionto support event schema versioning
- Serialization: Domain events define their JSON payloads with
JsonSerializable,asArray(), andfromJson(). - Event Sourcing Support: Traits to easily implement event recording and applying in your code.
- Attribute-based Configuration: Use PHP 8 attributes to map events to topics and identify correlation IDs.
- Topic Mapping: Tools to generate and use a mapping between topics and PHP classes.
Getting Started
1. Define a Domain Event
Implement the DomainEvent interface and use the MapToTopic attribute.
use spriebsch\DomainEvent\DomainEvent; use spriebsch\DomainEvent\JsonDomainEventTrait; use spriebsch\DomainEvent\MapToTopic; #[MapToTopic('my-vendor.my-domain.my-context.something_happened')] final readonly class SomethingHappened implements DomainEvent { use JsonDomainEventTrait; public function __construct( public string $data ) {} public function asArray(): array { return ['data' => $this->data]; } public static function fromJson(string $json): static { $payload = json_decode($json, true, 512, JSON_THROW_ON_ERROR); if (!is_array($payload) || !isset($payload['data']) || !is_string($payload['data'])) { throw new InvalidArgumentException('Expected a string data field'); } return new static($payload['data']); } }
2. Wrap the Event in an Envelope
The Envelope provides the metadata needed for handling the event.
use spriebsch\DomainEvent\Envelope; $event = new SomethingHappened('some data'); $envelope = Envelope::from($event); echo $envelope->topic()->asString(); // my-vendor.my-domain.my-context.something_happened echo $envelope->eventId()->asString(); // (a random UUIDv4)
3. Serialization
use spriebsch\DomainEvent\JsonDomainEventSerializer; use spriebsch\DomainEvent\JsonDomainEventDeserializer; $serializer = new JsonDomainEventSerializer(); $json = $serializer->serialize($event); $restored = new JsonDomainEventDeserializer()->deserialize($json, SomethingHappened::class);
json_encode($event, JSON_THROW_ON_ERROR) also works directly. Each event controls its payload
format; JsonDomainEventTrait makes jsonSerialize() return asArray().
4. Event Sourcing
Use the provided traits in your code.
use spriebsch\DomainEvent\CanRecordDomainEventsTrait; use spriebsch\DomainEvent\CanApplyDomainEventsTrait; final class MyDecision { use CanRecordDomainEventsTrait; use CanApplyDomainEventsTrait; public function doSomething(string $data): void { $this->record(new SomethingHappened($data)); } private function applySomethingHappened(SomethingHappened $event): void { // Update object's state } }
Installation
composer require spriebsch/domain-event
Development
You can use the php-devbox container (https://github.com/spriebsch/php-devbox) to run tests and static analysis.
Run Tests
php-devbox phpunit
Static Analysis
php-devbox phpstan