Unique event type constraint
Adorn the event type class with [Unique] to enforce that only one event of this type can be appended per event source. Use this when the event itself is a unique fact — for example, registering a user can only happen once.
Chronicle discovers [Unique]-adorned types automatically and registers the constraints with the Kernel when the client starts.
Defining a unique event type constraint
Section titled “Defining a unique event type constraint”using Cratis.Chronicle.Events;using Cratis.Chronicle.Events.Constraints;
[EventType][Unique]public record ConstraintsModelBoundUniqueEventTypeUserRegistered(string Email, string DisplayName);Kotlin does not support this workflow yet.Java does not support this workflow yet.defmodule MyApp.Events.ConstraintsModelBoundUniqueEventTypeUserRegistered do use Chronicle.Events.EventType, id: "constraints-model-bound-unique-event-type-user-registered"
defstruct [:email, :display_name]
unique_event_type()endTypeScript does not support this workflow yet.Constraint name and violation message
Section titled “Constraint name and violation message”An optional constraint name and violation message can be provided:
using Cratis.Chronicle.Events;using Cratis.Chronicle.Events.Constraints;
[EventType][Unique(name: "UniqueUser", message: "A user with this identity has already been registered.")]public record ConstraintsModelBoundUniqueEventTypeNamedUserRegistered(string Email, string DisplayName);Kotlin does not support this workflow yet.Java does not support this workflow yet.defmodule MyApp.Events.ConstraintsModelBoundUniqueEventTypeNamedUserRegistered do use Chronicle.Events.EventType, id: "constraints-model-bound-unique-event-type-named-user-registered"
defstruct [:email, :display_name]
# Elixir supports a custom constraint name, but not a custom violation message. unique_event_type(name: "UniqueUser")endTypeScript does not support this workflow yet.The message parameter is optional. Chronicle produces a default violation message when one is not supplied.
Releasing a constraint
Section titled “Releasing a constraint”Apply [RemoveConstraint] to the event type that signals a domain object has been removed. When this event is appended, Chronicle releases the constraint, and the event source can accept a new event of this type:
using Cratis.Concepts;using Cratis.Chronicle.Events;using Cratis.Chronicle.Events.Constraints;
public record ConstraintsModelBoundUniqueEventTypeUserId(Guid Value) : ConceptAs<Guid>(Value);
[EventType][RemoveConstraint("UniqueUser")]public record ConstraintsModelBoundUniqueEventTypeUserRemoved(ConstraintsModelBoundUniqueEventTypeUserId UserId);Kotlin does not support this workflow yet.Java does not support this workflow yet.defmodule MyApp.Events.ConstraintsModelBoundUniqueEventTypeUserRemoved do use Chronicle.Events.EventType, id: "constraints-model-bound-unique-event-type-user-removed"
defstruct [:user_id]
remove_constraint("UniqueUser")endTypeScript does not support this workflow yet.The constraint name must exactly match the name provided to the [Unique] attribute. When no name was provided to [Unique], Chronicle uses the event type name as the default constraint name.
How constraints are enforced
Section titled “How constraints are enforced”The Chronicle Kernel reads the [Unique] and [RemoveConstraint] attributes when the client connects and creates the indexes it needs. Every subsequent append is checked against those indexes server-side, so no constraint logic runs in client code.