Skip to main content

Metadata and personal data

Event metadata

Metadata is stored beside the event payload. EventMetadata supports optional correlation, causation, actor, tenant, and custom values:

$metadata = EventMetadata::empty()
->withCorrelationId($correlationId)
->withActorId($actorId)
->withCustom(['requestId' => $requestId]);

$eventWithMetadata = $event->withMetadata($metadata);

AmbientMetadataContext is a mutable, instance-based context for a request, message, or command. Call begin() at the start of a unit of work and end() after it. MetadataEnricher, passed to the facade, fills missing metadata when events are about to be stored; explicit event metadata wins over ambient defaults.

EventDispatcher can use the same context as a causation tracker. While a subscriber handles an event, events it causes can receive that event's ID as their causation ID.

Crypto-shredding

EncryptingEventEntryFactory decorates an existing entry factory. Mark the property carrying the data and the property identifying the data subject:

use DomainFlow\EventSourcing\Attribute\DataSubjectId;
use DomainFlow\EventSourcing\Attribute\PersonalData;

final class CustomerRegistered extends SourceEvent
{
public function __construct(
#[DataSubjectId]
public readonly string $subjectId,
#[PersonalData]
public readonly string $email,
?EntityIdentifierInterface $aggregateId = null,
) {
parent::__construct($aggregateId, null);
}
}

Personal-data properties must hold strings. If toArray() stores a property under another key, provide it as #[PersonalData(key: 'email_address')]. Values are encrypted with a per-subject key. Erasure destroys that key, leaves the append-only event unchanged, and returns RedactedValue::MARKER when the event is read afterwards.

Use PersonalDataEraser to forget a subject and delete snapshots for the aggregates known by the consumer to contain that subject's data:

$eraser->erase($subjectId, $aggregateId);

The key store must be durable in production. InMemoryPersonalDataKeyStore is only a reference implementation and test double. Deleting the key alone does not remove decrypted data from snapshots; configure snapshot storage/history so the eraser can delete those snapshots too.