Events and aggregates
Domain events
DomainEventInterface requires an aggregate identifier, occurrence timestamp,
stream version, array representation, and event metadata. It also allows the
infrastructure to assign a version and to create a copy with different
metadata.
For common events, extend SourceEvent. Its constructor accepts:
__construct(
?EntityIdentifierInterface $aggregateId,
?EntityIdentifierInterface $eventId,
?DateTimeImmutable $occurredOn = null,
?EventVersion $version = null,
)
Passing null generates an ID or timestamp. SourceEvent stores occurrence
times in UTC. If an event has additional payload fields, override toArray()
and merge the parent array, as shown in the quickstart.
Applying and replaying
AggregateRoot::applyEvent($event) does two things for a new event:
- it invokes the matching
apply<EventShortName>()method, if one exists; - it assigns the next stream version and adds the event to the uncommitted list.
AggregateRoot::reconstitute() creates an empty aggregate through the concrete
class's newInstance() method and applies stored events in stream order. During
replay, the stored event version is authoritative and the event is not marked as
uncommitted.
An aggregate therefore contains current state plus the events that still need to be stored:
$aggregate->getUncommittedEvents();
$aggregate->clearUncommittedEvents();
$aggregate->getAggregateVersion();
The facade and repository manage clearing uncommitted events after a successful storage call. Domain code normally only calls domain methods that emit events.
Stream versions
Stream versions are one-based. EventVersion::unassigned() is version 0 and
is used before an event has been applied to an aggregate. A newly applied event
gets version 1, the next gets version 2, and so on.
Do not confuse an event's stream version with its payload schema version. The
stream version identifies its position for one aggregate; the optional
_schemaVersion payload field identifies the shape of the event data.
Aggregate identifiers
An aggregate ID only needs to implement EntityIdentifierInterface. The package
provides AggregateId, backed by a DomainFlow UUIDv6, and the generic
EntityIdentifier implementation. Custom identifier value objects can be used
as long as they implement the interface and can be reconstructed from a string.