Identity¶
Every record written by rhizodat carries service fields:
| Field | Type | Meaning |
|---|---|---|
dat_id |
UUIDv7 | Entity identity. Generated by the layer, never by a store. Every part of one entity has the same dat_id in every store. |
dat_type |
string | Entity type, for stores that hold several types in one table or graph. |
dat_txn |
UUIDv7 | The meta-transaction that wrote this version. Used for idempotent apply and for filtering uncommitted data. |
dat_tx_time |
HLC timestamp | Transaction time of this version (see Time). |
UUIDv7 is time-ordered, which suits ORDER BY in ClickHouse, sorting in
Iceberg and B-tree locality in PostgreSQL.
UUIDv7 follows RFC 9562 with the 12-bit field used as a counter (section 6.2, method 1): ids from one generator are strictly increasing, also within one millisecond and when the wall clock steps back.
Storage per store: PostgreSQL uuid, ClickHouse UUID, drevo node property,
Iceberg uuid (falls back to fixed[16] where the engine lacks it).
Links between entities are references by dat_id. Foreign keys cannot
cross stores, so the Unit of Work checks referential integrity before commit.
Entity mapping¶
Which part of an entity lives in which store is declared per entity type; there is no central registry to look up.
from rhizodat import entity, Ref
@entity(stores={
"pg": ["status", "amount", "customer"],
"drevo": ["related"], # links as graph edges
"clickhouse": ["events"], # append-only facts
"iceberg": ["snapshots"], # long-term history
})
class Order:
status: str
amount: Decimal
customer: Ref["Customer"]
related: list[Ref["Order"]]
events: list[str]
snapshots: list[bytes]
The narrow case is one store; the wide case is all four. Both use the same API.
- Every declared field maps to exactly one store, and every mapped name is a
declared field.
ClassVars are not fields. dat_*names are reserved; so arevalid_from/valid_towhen valid time is on (@entity(..., valid_time=True)).- The type name stored as
dat_typedefaults to the class name; setname="tracker.Issue"to keep it stable across renames and language ports. @entitymakes the class a keyword-only dataclass with adat_idfield (a new UUIDv7 unless given).- A reference is
Ref[T], a value holding the target'sdat_id, also inside containers or| None. One field refers to one target type; a union of several is rejected in v0.1.