Skip to content

Event Context

Event context provides access to metadata about the event itself, such as when it occurred, its sequence number, who caused it and how it was correlated.

Access event context members with the $eventContext. prefix followed by a dotted path:

$eventContext.{path}

A path is written camelCase, as in the table below. Chronicle, which runs the projection, also resolves a segment whose first letter is uppercased ($eventContext.EventType.Id); no other spelling resolves.

Every path is checked against the event context catalog when the projection compiles. An unlisted member or sub-path is a warning (PLAY0295, PLAY0296); a runtime reflects over the event context and may resolve more than the catalog lists, but such a path is not portable. A path below a collection or an empty path is an error (PLAY0297, PLAY0298) because it can never resolve. See Diagnostics.

The table is generated from the catalog the compiler and the editor use, which mirrors Chronicle’s EventContext record.

PathTypeDescription
eventTypeEventTypeThe type of the event - its identifier, generation and whether it is a tombstone.
eventType.idEventTypeIdThe identifier of the event type.
eventType.generationEventTypeGenerationThe generation of the event type.
eventType.tombstoneboolWhether the event is a tombstone event.
eventSourceTypeEventSourceTypeThe type of the event source the event was appended to.
eventSourceIdEventSourceIdThe identifier of the event source the event was appended to - the value $eventSourceId resolves.
eventStreamTypeEventStreamTypeThe type of the event stream the event belongs to.
eventStreamIdEventStreamIdThe identifier of the event stream the event belongs to.
sequenceNumberEventSequenceNumberThe position of the event in its event sequence.
occurredDateTimeOffsetWhen the event occurred - the time it was appended unless one was given.
occurred.WeekintThe ISO 8601 week of the year the value falls in.
eventStoreEventStoreNameThe name of the event store the event belongs to.
namespaceEventStoreNamespaceNameThe namespace within the event store the event belongs to.
correlationIdCorrelationIdThe correlation identifier the event was appended with.
causationIEnumerable<Causation>The chain of causes that led to the event - a collection, not addressable below.
causedByIdentityThe identity that caused the event.
causedBy.subjectstringThe subject identifier of the identity.
causedBy.namestringThe display name of the identity.
causedBy.userNamestringThe user name of the identity.
causedBy.onBehalfOfIdentityThe identity this identity acted on behalf of, if any - an identity with the same members.
tagsIEnumerable<Tag>The tags the event was appended with - a collection, not addressable below.
hashEventHashThe hash of the event content.
observationStateEventObservationStateWhether the observer sees the event for the first time or again on replay - differs between live processing and replay.
subjectSubjectThe compliance subject the event is about - the event source identifier unless one was given.
subjectIsEventSourceIdboolWhether the subject is the event source identifier.
  • causation and tags are collections. There is no syntax to address an element, so $eventContext.causation.occurred is an error.
  • A member typed by a concept - such as eventSourceId, correlationId, sequenceNumber or eventType.id - also exposes its underlying .value, such as $eventContext.correlationId.value. The table leaves those rows out.
  • Week is a derived function, not a member of the value. It is case-sensitive: $eventContext.occurred.Week (or Week()) resolves, $eventContext.occurred.week does not.
  • observationState differs between live processing and a replay of the same event, so it is unstable as read model data.
  • causedBy.onBehalfOf is itself an identity with the same members, to any depth.

The timestamp when the event occurred:

from UserRegistered
Name = name
CreatedAt = $eventContext.occurred

The ISO 8601 week the event occurred in:

from TimeRecorded
Week = $eventContext.occurred.Week

The position of the event in its event sequence:

from EventProcessed
LastSequenceNumber = $eventContext.sequenceNumber

The correlation ID associated with the event:

from OrderPlaced
OrderId = orderId
CorrelationId = $eventContext.correlationId

The type of the event, with its id, generation and tombstone:

from OrderPlaced
LastEventType = $eventContext.eventType.id

The identity that caused the event, with its subject, name, userName and onBehalfOf:

from OrderPlaced
PlacedBy = $eventContext.causedBy.subject
PlacedByName = $eventContext.causedBy.name

The short form $causedBy.subject also parses, and Chronicle compiles it, but Chronicle cannot yet evaluate it when the projection runs (Cratis/Chronicle#4119). Write $eventContext.causedBy.<property> until that is fixed.

The event source ID:

from UserAssignedToGroup
GroupId = $eventContext.eventSourceId

$eventSourceId and $eventContext.eventSourceId

Section titled “$eventSourceId and $eventContext.eventSourceId”

$eventSourceId is not shorthand for $eventContext.eventSourceId. They are two different expressions - each its own node in the syntax tree, and each its own expression in Chronicle - that resolve to the same value: the identifier of the event source the event was appended to. Prefer $eventSourceId, and use it in keys:

from UserCreated key $eventSourceId
Name = name

A mapping target can end in a dynamic key - a static property path followed by .$eventContext.<path>. The value at that path becomes the dictionary key the mapping writes under, so one property holds one entry per distinct value:

projection EventStatistics => EventStatisticsReadModel
all
count eventCountByType.$eventContext.eventType.id
increment eventsPerSource.$eventContext.eventSourceId

The path is checked against the same catalog. Only $eventContext is resolved in a key; any other $ source, such as countByUser.$causedBy.subject, is kept as the literal key text, which is a warning (PLAY0299). Write countByUser.$eventContext.causedBy.subject instead.

$eventContext and $context are separate namespaces. $eventContext reads the event being projected and is available in projections. $context reads the command or query being handled and is available in produces and capture mappings. Neither falls back to the other, and they are checked against different catalogs.

The executable semantic model binds a projection value only when it is one portable scalar. Of the paths above, it admits every member that holds a single value with a portable type, written in its canonical camelCase spelling - .value on a concept and an uppercased first letter both bind as the plain member, and eventSourceId binds as the event source identity. It does not admit:

  • the collections causation and tags,
  • eventType and causedBy without a member below them,
  • occurred.Week, which is a function Chronicle computes rather than a value the event carries,
  • paths through causedBy.onBehalfOf, which is optional,
  • observationState, which has no portable type and differs between live processing and replay.

The reference executor does not evaluate event-context values yet; a projection that reads one compiles to a plan that reports it.

Track when things happen:

from RecordCreated
Name = name
CreatedAt = $eventContext.occurred
from RecordUpdated
Name = name
UpdatedAt = $eventContext.occurred

Link related operations:

from OrderPlaced
OrderNumber = orderNumber
CorrelationId = $eventContext.correlationId
PlacedAt = $eventContext.occurred

Track event order:

from StateChanged
CurrentState = state
SequenceNumber = $eventContext.sequenceNumber
ChangedAt = $eventContext.occurred

Use the event source ID for relationships:

children members identified by userId
from UserAddedToGroup key userId
parent $eventContext.eventSourceId
Role = role
AddedAt = $eventContext.occurred

Event context can be used in composite keys:

from EventProcessed
key ProcessingKey
EventId = eventId
SequenceNumber = $eventContext.sequenceNumber
CorrelationId = $eventContext.correlationId
ProcessedAt = $eventContext.occurred

Event context works in string templates:

from OrderShipped
TrackingInfo = `Shipped at ${$eventContext.occurred}`
Reference = `${orderNumber}-${$eventContext.sequenceNumber}`
projection UserActivity => UserActivityReadModel
from UserLoggedIn
LastLogin = $eventContext.occurred
LastLoginSequence = $eventContext.sequenceNumber
count LoginCount
from UserAction
LastAction = actionType
LastActionTime = $eventContext.occurred
LastCorrelation = $eventContext.correlationId
projection Document => DocumentReadModel
from DocumentCreated
Title = title
Content = content
Version = $eventContext.sequenceNumber
CreatedAt = $eventContext.occurred
from DocumentUpdated
Content = content
Version = $eventContext.sequenceNumber
UpdatedAt = $eventContext.occurred
projection Group => GroupReadModel
from GroupCreated
Name = name
CreatedAt = $eventContext.occurred
CreatedBy = $eventContext.eventSourceId
children members identified by userId
from UserAddedToGroup key userId
parent groupId
Name = userName
Role = role
AddedAt = $eventContext.occurred
AddedSequence = $eventContext.sequenceNumber
projection Order => OrderReadModel
from OrderPlaced
OrderNumber = orderNumber
CustomerId = customerId
Total = total
PlacedAt = $eventContext.occurred
TrackingId = $eventContext.correlationId
from OrderShipped
ShippedAt = $eventContext.occurred
ShipmentSequence = $eventContext.sequenceNumber
Status = "Shipped"
  1. Audit Timestamps: Use occurred for created/updated timestamps
  2. Correlation: Use correlationId to link related operations
  3. Versioning: Use sequenceNumber for version tracking
  4. Relationships: Use $eventSourceId for parent-child relationships
  5. Debugging: Include correlation IDs and timestamps for troubleshooting
  6. Stability: Event context values are fixed when the event is appended - except observationState, which depends on whether the event is being replayed