Macro for defining Chronicle concepts — strongly-typed wrappers over a
single primitive value, mirroring ConceptAs<T> in the C# client and
ConceptAs in the Kotlin client.
A concept lets you declare a domain value's type — and, critically, its
compliance classification — exactly once, on the type itself, instead of
repeating a pii/1,2 annotation on every event or read model field that
happens to hold that value.
Defining a concept
defmodule MyApp.PersonName do
use Chronicle.Concept, type: :string
pii "Legal name of the data subject"
endAny struct field across your event types and read models that defaults to
%MyApp.PersonName{} is now automatically recognized by
Chronicle.Schemas.JsonSchemaGenerator as carrying PII — see that module's
documentation for how type-level compliance is resolved and pushed down
onto schema leaves.
Options for use Chronicle.Concept
:type— (required) the primitive type the concept wraps. One of:string,:integer,:float,:boolean, or:uuid. Any other value raisesArgumentErrorat compile time.:event_source_id— whentrue, marks this concept as representing a Chronicle event source identifier, mirroringEventSourceId<T>in the C# client. Defaults tofalse. A concept declared withevent_source_id: truecannot also declarepii/0,1— see "PII is not supported on an event source id" below.
What use Chronicle.Concept generates
defstruct value: <default>— a single-field struct wrapping the declared type's sensible zero value (""for:stringand:uuid,0for:integer,0.0for:float,falsefor:boolean).__chronicle_concept__/1— introspection, accepting:type,:event_source_id?, or:piias the key.- A
Jason.Encoderimplementation that serializes the concept as its wrapped value, not as an object wrapping one —Jason.encode!(%MyApp.PersonName{value: "Ada"})produces"Ada", exactly asJason.encode!("Ada")would. This is critical: it is what lets an existing property already in production be adopted into a concept without changing the wire format or the registered schema, mirroring the intent of Kotlin'sConceptTypeAdapterFactoryand the KDoc onio.cratis.chronicle.concepts.ConceptAs. __chronicle_pii__/0— present on every concept module (empty when nopii/0,1was declared), soChronicle.Schemas.JsonSchemaGeneratorcan treat every concept module uniformly, the same way it already treatsChronicle.Events.EventTypeandChronicle.ReadModels.ReadModelmodules.
Declaring PII on a concept
Use pii/0 or pii/1 — not Chronicle.Compliance.pii/1,2, which
expects a field name as its first argument. A concept has exactly one
field (:value), so its pii/0,1 takes only the optional details text:
defmodule MyApp.NationalId do
use Chronicle.Concept, type: :string
pii "National ID number — sensitive personal identifier"
endPII is not supported on an event source id
Chronicle uses the event source id to look up the encryption key for a
subject's PII values. A concept cannot be both the key used to find the
encryption key and one of the values encrypted under it, so declaring
pii/0,1 on a concept created with event_source_id: true raises
ArgumentError at compile time — mirroring the C# client's
PIINotSupportedOnEventSourceId:
defmodule MyApp.EmployeeId do
use Chronicle.Concept, type: :uuid, event_source_id: true
pii "this raises ArgumentError"
endIf the identifier itself is sensitive, use a non-sensitive surrogate
(event_source_id: true, no pii) as the event source id, and store the
sensitive value in a separate PII-marked concept or event property.
Registering a value
A concept is a plain struct — construct it like any other:
%MyApp.PersonName{value: "Ada Lovelace"}
Summary
Callbacks
Returns metadata for this concept module.
Functions
Marks this concept's wrapped value as containing Personally Identifiable Information (PII).
Callbacks
@callback __chronicle_concept__(key :: :type | :event_source_id? | :pii) :: term()
Returns metadata for this concept module.
Accepts :type, :event_source_id?, or :pii as the key.
Functions
Marks this concept's wrapped value as containing Personally Identifiable Information (PII).
details is an optional human-readable explanation of why the value is
classified as PII and defaults to an empty string. Cannot be combined with
event_source_id: true — see the module documentation.