Identities of entities and aggregates, and generating them.
The identity string
An entity's identity is made of one or more fields, declared with one identity/2 each:
identity(:tenant_id, :uuid)
identity(:sku, :string)Entity.identity(entity) returns it as a string: the value itself for a single field, the
values joined with : for several ("6f1f...:A-100"). A \ or : inside a part is escaped
(\\, \:) so the string always splits back into the same parts (decode/1). The string
is what find_by_id/1, the outbox aggregate_id and a {:ref, Aggregate} to a composite
aggregate use.
Entity.build_identity(parts) makes the same string from a map or keyword list of the parts,
without an entity. Entity.identity_fields() lists the part names.
A composite identity part must be a :string, :uuid, :integer, :atom, :date or
enum field. Every part is required, and none may change once the entity exists.
Generating ids
new/1 fills in a UUIDv7 only for a single identity(:id, :uuid) that the caller left out.
For any other identity the caller passes every part. generate/0 and the functions below
make ids by hand:
Summary
Functions
Build the identity string for module from a map or keyword list holding its parts. Raises
ArgumentError when a part is missing or nil.
Split a composite identity string back into its parts (the inverse of encode/1), also when a
part itself contains a :.
Join identity parts into the identity string. One part is returned as text unchanged; several
are escaped and joined with :.
A new id: a UUIDv7.
The identity string of an entity instance, for naming it in an error, or nil when it has
none or can't be told (a part is missing or has no text form). Never raises.
The value a repository stores an entity under: the field value for a single-field identity,
the identity string for a composite one. nil for a struct without identity.
Fills in a generated :id when the identity is a lone identity(:id, :uuid) and the caller
left it out. Any other identity is returned untouched. A function rather than inline code in
the generated new/1, so a value object (no identity) does not get a clause that can never
match.
A UUIDv1 (time and node based).
A name-based UUIDv3 (MD5). namespace is :dns, :url, :oid, :x500 or a UUID.
A random UUIDv4.
A name-based UUIDv5 (SHA-1). namespace is :dns, :url, :oid, :x500 or a UUID.
A UUIDv7: a 48-bit millisecond timestamp followed by random bits.
Functions
Build the identity string for module from a map or keyword list holding its parts. Raises
ArgumentError when a part is missing or nil.
Split a composite identity string back into its parts (the inverse of encode/1), also when a
part itself contains a :.
iex> DDD.Identity.decode("t1:sku-9")
["t1", "sku-9"]
iex> ["a:b", "c"] |> DDD.Identity.encode() |> DDD.Identity.decode()
["a:b", "c"]
Join identity parts into the identity string. One part is returned as text unchanged; several
are escaped and joined with :.
iex> DDD.Identity.encode(["order-7"])
"order-7"
iex> DDD.Identity.encode(["t1", "sku-9"])
"t1:sku-9"
@spec generate() :: String.t()
A new id: a UUIDv7.
The identity string of an entity instance, for naming it in an error, or nil when it has
none or can't be told (a part is missing or has no text form). Never raises.
The value a repository stores an entity under: the field value for a single-field identity,
the identity string for a composite one. nil for a struct without identity.
Fills in a generated :id when the identity is a lone identity(:id, :uuid) and the caller
left it out. Any other identity is returned untouched. A function rather than inline code in
the generated new/1, so a value object (no identity) does not get a clause that can never
match.
@spec uuid1() :: String.t()
A UUIDv1 (time and node based).
A name-based UUIDv3 (MD5). namespace is :dns, :url, :oid, :x500 or a UUID.
@spec uuid4() :: String.t()
A random UUIDv4.
A name-based UUIDv5 (SHA-1). namespace is :dns, :url, :oid, :x500 or a UUID.
@spec uuid7() :: String.t()
A UUIDv7: a 48-bit millisecond timestamp followed by random bits.
Ids made in different milliseconds sort in creation order. Ids made within the same millisecond are unique but not ordered.