Macro for defining Chronicle event types.
Use Chronicle.Events.EventType in an event struct module to annotate it with a
stable event type identifier and generation number. Chronicle uses these to
register the event schema and route events to the correct observers.
You can also declare model-bound constraints directly on the event type through attributes:
defmodule MyApp.Events.UserRegistered do
use Chronicle.Events.EventType, id: "user-registered-v1"
defstruct [:email]
@unique :email
unique_event_type()
end
defmodule MyApp.Events.UserDeleted do
use Chronicle.Events.EventType, id: "user-deleted-v1"
defstruct [:email]
@remove_constraint "email"
endUsage
defmodule MyApp.Events.AccountOpened do
use Chronicle.Events.EventType, id: "account-opened-v1"
defstruct account_id: "", owner_name: "", initial_balance: 0
endWith an explicit generation:
defmodule MyApp.Events.FundsDeposited do
use Chronicle.Events.EventType, id: "funds-deposited", generation: 2
defstruct account_id: "", amount: 0, currency: ""
endIntrospection
Modules that use Chronicle.Events.EventType expose metadata via
__chronicle_event_type__/1:
MyApp.Events.AccountOpened.__chronicle_event_type__(:id)
#=> "account-opened-v1"
MyApp.Events.AccountOpened.__chronicle_event_type__(:generation)
#=> 1Options
:id— the stable event type identifier (required, unless:generation_foris given):generation— the generation number (default1; required when:generation_foris given):generation_for— marks this module as a previous generation of the event type module it names, instead of giving it its own:id. The event type id is resolved from the target module, so the two generations can never drift apart by a hand-typed, mismatched id string. Cannot be combined with:id; the target module must itselfuse Chronicle.Events.EventType.defmodule MyApp.Events.AccountOpenedV1 do use Chronicle.Events.EventType, generation_for: MyApp.Events.AccountOpened, generation: 1 defstruct account_id: "", owner_name: "" end:subject— the field holding the event's data subject, the person whose PII the event carries (subject: :customer_id). It is resolved on append unless an explicit:subjectoption is given, and decides whose erasure key protects the event's PII. Without it the kernel falls back to the event source id.:tags— tags every append of this event type carries, merged with per-append:tags:tombstone— marks the event type as a tombstone (the last event of an event source's life) (mirrors .NET's[Tombstone]):public— makes the event type visible to other event stores, e.g. through event store subscriptions (mirrors[Public]):compensation_for— the event type module this event compensates for (mirrors[CompensationFor<T>]):types— type hints for nested fields; seeChronicle.Serialization
When you append an event, the client encodes its fields as camelCase JSON:
owner_name is stored as ownerName. Give every field a typed default, such
as initial_balance: 0; the JSON schema Chronicle validates appends against is
generated from the defaults, and a nil default registers the field as a
string.
Summary
Callbacks
Returns metadata for this event type module.
Functions
Declares that appending this event closes a stream scope (a closing event).
Resolves the data subject of an event from its declared :subject field, if it has one.
The tags every append of this event type carries (declared with tags:).
Returns whether module is a Chronicle event type (use Chronicle.Events.EventType).
Declares that appending this event removes a named constraint.
Declares that appending this event removes a named constraint, carrying the value to release.
Declares a unique constraint for one or more event fields.
Declares a unique constraint for the whole event type.
Callbacks
@callback __chronicle_event_type__( key :: :id | :generation | :generation_for | :constraints | :pii | :encrypted | :subject | :tags | :tombstone | :public | :compensation_for ) :: term()
Returns metadata for this event type module.
Accepts :id, :generation, or :constraints as the key.
Functions
Declares that appending this event closes a stream scope (a closing event).
The kernel writes the event and the closure together; every later event inside the closed
scope is refused with a stream-closed violation, while events elsewhere are unaffected. Use it
when a domain fact ends something: a delivered report, closed books, a superseded version.
Mirrors .NET's [ClosesStream]. Supported opts:
:dimensions- the participating dimensions, a list of:event_source_id,:event_source_type,:event_stream_typeand:event_stream_id. Defaults to[:event_source_id, :event_stream_type, :event_stream_id].:event_stream_id_from- the event field supplying the stream id instead of the append (implies the:event_stream_iddimension):reopened_by- event type modules that lift the closure of exactly this scope:name- the owning constraint name (defaults to the event type id); declarations that share a name merge, and must use the same dimensions and stream id field:event_sequences- restrict the declaration to specific event sequences (default: all)
Registering a declaration closes nothing in history; only later appends close scopes.
defmodule MyApp.Events.PeriodClosed do
use Chronicle.Events.EventType, id: "period-closed"
defstruct period: ""
closes_stream event_stream_id_from: :period, reopened_by: [MyApp.Events.PeriodReopened]
end
Resolves the data subject of an event from its declared :subject field, if it has one.
An event type declares the field holding the subject (the person whose PII it carries)
with use Chronicle.Events.EventType, id: "...", subject: :customer_id. A Chronicle.Concept
value is unwrapped. Returns nil when no subject is declared or the field is empty.
The tags every append of this event type carries (declared with tags:).
Returns whether module is a Chronicle event type (use Chronicle.Events.EventType).
Every append path checks this before building a request, so an event struct that never
registered an event type fails with a structured {:error, {:unknown_event_type, module}}
instead of raising deep inside request building.
Declares that appending this event removes a named constraint.
This is equivalent to setting @remove_constraint.
Declares that appending this event removes a named constraint, carrying the value to release.
:properties names the constrained properties, in the same order as the unique/2
declaration, that this event carries the value of (empty or omitted releases every value for
the event source, same as remove_constraint/1). Only meaningful for a :per_value unique
constraint — a unique event type constraint has no constrained value to carry.
defmodule MyApp.Events.MappingVersionRemoved do
use Chronicle.Events.EventType, id: "mapping-version-removed"
defstruct [:version_id]
remove_constraint("UniqueMappingVersionId", properties: [:version_id])
end
Declares a unique constraint for one or more event fields.
This is equivalent to setting @unique. Supported opts:
:name— explicit constraint name (defaults to the first field name):mode—:per_event_source(default) keeps one value per event source and releases the previous value when it is replaced;:per_valuekeeps every value until a removal event (remove_constraint/1) releases it:ignore_casing— compare values case-insensitively:message— human-readable message to use when the constraint is violated:scope— narrows uniqueness checking to values observed at append time along one or more dimensions, mirroring the C# client'sIConstraintBuilder.PerEventSourceType()/PerEventStreamType()/PerEventStreamId(). An atom (:per_event_source_type,:per_event_stream_type, or:per_event_stream_id) or a list of them. Defaults to unscoped (globally unique across the whole event store).:event_sequences— restrict the declaration to specific event sequences (default: all)
:message also accepts a {Module, :function} callback invoked with the violation to
compute one; see Chronicle.Events.ConstraintViolation.details/1.
An unknown option, an invalid :mode, or an invalid :scope value emits a compile warning
(the declaration still compiles; an unknown :mode then raises ArgumentError when the
constraint definition is built, as in earlier releases).
Scoping example
# Unique only within events for the same event source type — the same
# value may repeat across different event source types.
unique(:email, scope: :per_event_source_type)
Declares a unique constraint for the whole event type.
This is equivalent to setting @unique_event_type. Supported opts:
:name— explicit constraint name (defaults to the event type id):message— human-readable message to use when the constraint is violated, or a{Module, :function}callback; seeunique/2:scope— seeunique/2:event_sequences— seeunique/2
An unknown option or an invalid :scope value emits a compile warning.