# `Chronicle.Concept`
[🔗](https://github.com/Cratis/Chronicle.Elixir/blob/main/lib/chronicle/concept.ex#L4)

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"
    end

Any 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 raises `ArgumentError` at compile time.
  * `:event_source_id` — when `true`, marks this concept as representing a
    Chronicle event source identifier, mirroring `EventSourceId<T>` in the
    C# client. Defaults to `false`. A concept declared with
    `event_source_id: true` cannot also declare `pii/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 `:string` and `:uuid`,
    `0` for `:integer`, `0.0` for `:float`, `false` for `:boolean`).
  * `__chronicle_concept__/1` — introspection, accepting `:type`,
    `:event_source_id?`, or `:pii` as the key.
  * A `Jason.Encoder` implementation that serializes the concept as its
    **wrapped value**, not as an object wrapping one — `Jason.encode!(%MyApp.PersonName{value: "Ada"})`
    produces `"Ada"`, exactly as `Jason.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's
    `ConceptTypeAdapterFactory` and the KDoc on `io.cratis.chronicle.concepts.ConceptAs`.
  * `__chronicle_pii__/0` — present on every concept module (empty when no
    `pii/0,1` was declared), so `Chronicle.Schemas.JsonSchemaGenerator` can
    treat every concept module uniformly, the same way it already treats
    `Chronicle.Events.EventType` and `Chronicle.ReadModels.ReadModel` modules.

## 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"
    end

## PII 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"
    end

If 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.

## Encrypted is not supported on an event source id either

Exactly the same reasoning applies to `Chronicle.Confidentiality.encrypted/0,1`:
Chronicle uses the event source id to look up the encryption key, so a
concept created with `event_source_id: true` cannot also declare
`encrypted/0,1` - it raises `ArgumentError` at compile time, mirroring
the restriction above.

## PII and Encrypted are mutually exclusive

A concept cannot declare both `pii/0,1` and `Chronicle.Confidentiality.encrypted/0,1`.
A value needs exactly one protection - combining them would encrypt it
twice, under two different keys, and it could never be released
correctly. Declaring both raises `ArgumentError` at compile time.

## Registering a value

A concept is a plain struct — construct it like any other:

    %MyApp.PersonName{value: "Ada Lovelace"}

# `__chronicle_concept__`

```elixir
@callback __chronicle_concept__(key :: :type | :event_source_id? | :pii | :encrypted) ::
  term()
```

Returns metadata for this concept module.

Accepts `:type`, `:event_source_id?`, `:pii`, or `:encrypted` as the key.

# `encrypted`
*macro* 

Marks this concept's wrapped value as needing plain-confidentiality
encryption at rest - the concept-level counterpart to
`Chronicle.Confidentiality.encrypted/1,2,3`, mirroring how `pii/0,1` is the
concept-level counterpart to `Chronicle.Compliance.pii/1,2`.

  * `scope` — the `EncryptionScope` the key is provisioned under: `:subject`
    (default), `:namespace`, or `:global`.
  * `details` — an optional human-readable explanation of why the value
    needs encryption, and defaults to an empty string.

Cannot be combined with `event_source_id: true`, or with `pii/0,1` on the
same concept — see the module documentation.

# `pii`
*macro* 

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`, or with `encrypted/0,1` on the same concept — see
the module documentation.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
