Search by

spriebsch / domain-event

spriebsch

Domain Events, Done Right.

Package info

github.com/spriebsch/domain-event

pkg:composer/spriebsch/domain-event

Statistics

Installs: 84

Dependents: 2

Suggesters: 0

Stars: 1

Open Issues: 0

3.0.0 2026-10-17 07:44 UTC

This package is auto-updated.

Last update: 2026-10-17 07:47:52 UTC


README

Domain Events, Done Right.

Features

  • Structured Topics: Define topics using a vendor.domain.context.name format.
  • Event Enveloping: Wrap domain events in an Envelope that carries essential metadata:
    • EventId (UUIDv4) to uniquely identify the event
    • Timestamp (Received and Persisted)
    • Topic
    • optional CausationId and CorrelationId for tracing
    • an optional SchemaVersion to support event schema versioning
  • Serialization: Domain events define their JSON payloads with JsonSerializable, asArray(), and fromJson().
  • 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