Demand: Data Model
The Demand module stores two occurrence-carrying bitemporal tables — demand_item and production_delivery — each appending a version row per mutation rather than editing in place. Its subjects are a demand item (a card-cycle collation task: what’s wanted, how much, which kanban cards are attached, what’s been produced against it) and a production delivery (one recorded production event against a demand item). This page is the field-complete reference for both tables and their DTOs; see Data Models — Reading Guide for the shared envelope shapes referenced throughout.
Entities and relationships
Section titled “Entities and relationships”production_delivery is demand’s own child table (demand_ref, same-module, no FK). demand_item carries three floating references out of the module: to the item it produces, to the kanban cards it collates (membership), and to inventory pools (a single holding_ref pool plus a per-member member_pools map — an interim Phase-3 bridge).
Stored shape
Section titled “Stored shape”demand_item
Section titled “demand_item”Occurrence columns were renamed by V004 (kind→occ_kind, outcome→occ_outcome, reason→occ_reason); the Kotlin property names are unchanged.
| Column | Type | Null | Role |
|---|---|---|---|
id | UUID | NOT NULL (PK) | identity |
tenant_id | UUID | NOT NULL | identity |
effective_as_of | timestamptz | NOT NULL | identity |
recorded_as_of | timestamptz | NOT NULL, default now() | identity |
previous | UUID | NULL | identity |
eid | UUID | NOT NULL | identity |
retired | boolean | NOT NULL, default false | bookkeeping |
bts_author | varchar(255) | NOT NULL | bookkeeping |
bts_author_sub | varchar(255) | NULL | bookkeeping |
bts_created_by_sub | varchar(255) | NULL | bookkeeping |
bts_note | varchar(512) | NULL | payload |
created_by | varchar(255) | NOT NULL, default ‘system’ | bookkeeping |
created_at_effective | timestamptz | NOT NULL, default now() | bookkeeping |
created_at_recorded | timestamptz | NOT NULL, default now() | bookkeeping |
item_reference_entity_id | UUID | NULL | payload |
item_reference_item_name | varchar(255) | NULL | bookkeeping |
item_reference_record_id | UUID | NULL | bookkeeping — reserved, unwritten; see Schema notes |
item_reference_retired | boolean | NOT NULL, default false | bookkeeping |
item_reference_provenance_updated_by | varchar(255) | NULL | bookkeeping |
item_reference_provenance_updated_at | bigint | NULL | bookkeeping |
station_eid | UUID | NOT NULL | payload — opaque, never resolved |
occ_kind (was kind) | varchar(64) | NOT NULL | payload |
occ_outcome (was outcome) | varchar(16) | NOT NULL | payload |
occ_reason (was reason) | varchar(64) | NULL, set iff REFUSED | payload |
notification_id | UUID | NULL | payload |
status | varchar(32) | NOT NULL | payload |
demand_qty | JSONB | NOT NULL | payload |
membership | JSONB | NOT NULL | payload — full set of attached CardCycle eIds |
holding_ref | UUID | NULL | payload — the holding InventoryPool eId, set by Start Production |
payload | JSONB | NULL | payload — per-kind relation-shaped payload (e.g. Distribute allocation) |
member_pools | JSONB | NOT NULL, default {} | payload — member CardCycle eId → member InventoryPool eId, interim Phase-3 bridge |
Unique constraints: demand_item_committed_previous — UNIQUE partial index on (previous) WHERE occ_outcome = 'COMMITTED', the P2-H1 committed-successor fork guard.
production_delivery
Section titled “production_delivery”Demand’s second occurrence-carrying table (PDEV-1528). Same envelope shape as demand_item; occurrence columns renamed identically by V004.
| Column | Type | Null | Role |
|---|---|---|---|
id | UUID | NOT NULL (PK) | identity |
tenant_id | UUID | NOT NULL | identity |
effective_as_of | timestamptz | NOT NULL | identity |
recorded_as_of | timestamptz | NOT NULL, default now() | identity |
previous | UUID | NULL | identity |
eid | UUID | NOT NULL | identity |
retired | boolean | NOT NULL, default false | bookkeeping |
bts_author | varchar(255) | NOT NULL | bookkeeping |
bts_author_sub | varchar(255) | NULL | bookkeeping |
bts_created_by_sub | varchar(255) | NULL | bookkeeping |
bts_note | varchar(512) | NULL | payload |
created_by | varchar(255) | NOT NULL, default ‘system’ | bookkeeping |
created_at_effective | timestamptz | NOT NULL, default now() | bookkeeping |
created_at_recorded | timestamptz | NOT NULL, default now() | bookkeeping |
demand_ref | UUID | NOT NULL | identity — same-module parent link |
item_eid | UUID | NOT NULL | payload |
quantity | JSONB | NOT NULL | payload — immutable at Create |
source_handle | varchar(128) | NULL | payload — never set; see Schema notes |
occ_kind (was kind, widened) | varchar(64) | NOT NULL | payload |
occ_outcome (was outcome) | varchar(16) | NOT NULL | payload |
occ_reason (was reason) | varchar(64) | NULL, set iff REFUSED | payload |
status | varchar(16) | NOT NULL | payload — CREATED / REVOKED |
Unique constraints: production_delivery_committed_previous — the same P2-H1 fork guard, UNIQUE partial index on (previous) WHERE occ_outcome = 'COMMITTED'.
Projected shape
Section titled “Projected shape”DemandItemPayload
Section titled “DemandItemPayload”| Field | Type | Stored or computed | Notes |
|---|---|---|---|
eId | EntityId | Stored | ← eid |
item | ItemReference | Stored | the item_reference_* cluster |
stationEId | UUID | Stored | |
kind | DemandOccurrenceKind | Stored | ← occ_kind |
outcome | DemandOutcome | Stored | ← occ_outcome |
reason | String? | Stored | |
notificationId | UUID? | Stored | |
status | DemandStatus | Stored | |
demandQty | GeneralizedQuantity | Stored | ← demand_qty |
membership | Set<UUID> | Stored | |
holdingRef | UUID? | Stored | |
payload | JsonElement? | Stored | |
memberPools | Map<UUID,UUID> | Stored |
ProductionDeliveryPayload
Section titled “ProductionDeliveryPayload”| Field | Type | Stored or computed | Notes |
|---|---|---|---|
eId | EntityId | Stored | |
demandRef | UUID | Stored | |
itemEId | UUID | Stored | |
quantity | GeneralizedQuantity | Stored | |
sourceHandle | String? | Stored | never populated in practice — see Schema notes |
kind | ProductionDeliveryOccurrenceKind | Stored | |
outcome | DemandOutcome | Stored | |
reason | String? | Stored | |
status | ProductionDeliveryStatus | Stored |
DemandView (internal read projection — not directly serialized by any endpoint)
Section titled “DemandView (internal read projection — not directly serialized by any endpoint)”| Field | Type | Stored or computed | Notes |
|---|---|---|---|
item | DemandItem | Computed | DemandItemPayload.toView() |
state | DemandState | Computed | status, demandQty, membership, holding re-typed from the row |
rId | DemandItemRId | Computed | the row’s own rId |
at | TimeCoordinates | Computed | the row’s own bitemporal coordinates |
DemandCommitResponse (mutation responses)
Section titled “DemandCommitResponse (mutation responses)”| Field | Type | Notes |
|---|---|---|
occurrence | OccurrenceDto | see Data Models — Reading Guide |
record | EntityRecord<DemandItemPayload, DemandItemMetadata> | full row projection, no hand-rolled view |
Cross-module references
Section titled “Cross-module references”| Column | Target | Kind | Notes |
|---|---|---|---|
item_reference_entity_id | Item | floating | guard-time liveness check re-reads by eId on every genesis |
item_reference_item_name, _retired, _provenance_* | Item | value-snapshot | only entity_id + item_name populated as of Increment 3; retired/provenance await observer-driven sync |
item_reference_record_id | Item version row | reserved version-pin slot | never populated — see Schema notes |
station_eid | resources.station.Station | floating in shape, opaque in practice | never resolved; pinned to == tenantId as validation only |
membership (JSONB set) | Kanban Cards — CardCycle | floating | each entry is a card’s own eId; re-resolved on every read, never pinned |
member_pools (map values) | Inventory Pools | floating | member eId → pool eId; re-resolved each call |
holding_ref | Inventory Pools | floating | minted once by Start Production, re-resolved by eId on every content read |
production_delivery.item_eid | Item | floating | compared against demand_item’s item for the RWrongItem guard; never fetched as a record |
production_delivery.source_handle | (external producing process) | not a modeled reference | opaque, unresolved reconciliation handle |
production_delivery.demand_ref is a same-module parent-child link (1:N, stores the DemandItem’s eId, no FK). Note: RecordProductionRequest.deliveryPool is read and merged into the holding pool but isn’t stored anywhere — no persisted relationship exists for that leg once the merge completes.
API surface
Section titled “API surface”Mounted under /v1/operations/demand/demand-item/.... Follows the request/response call-shape convention in The Orders v2 Call Shape.
| Method | Path | Purpose | Main refusals |
|---|---|---|---|
| POST | create | Create a card-less demand task | |
| POST | adjust-qty | Set the advisory intent total | |
| POST | reset-qty | Snap intent to members’ sum | |
| POST | release / reopen | OPEN ↔ RELEASED | |
| POST | cancel | OPEN, no cards → CANCELED | |
| POST | delete | Tombstone a terminal task | |
| POST | create-with-cycle | Create from its first card | |
| POST | add-cycle / remove-cycle | Adjust card membership | |
| POST | detach-withdrawn | Reconcile a withdrawn attachment (manual repair) | |
| POST | start-production | Mint the holding pool | |
| POST | record-production | Record a production delivery | wrong item |
| POST | revoke-delivery | Terminally revoke a recorded delivery | |
| POST | distribute | Allocate accumulated production to members | |
| POST | complete | IN_PROCESS → COMPLETE | |
| GET | for?item&station | Live demand items for an (item, station) pair | |
| GET | holding-card/{cardEId}?effectiveasof&recordedasof | The live demand holding a member card | 404 if none |
| GET | {eId}?effectiveasof&recordedasof | A demand item as of a coordinate | tombstoned = 404 |
| GET | {eId}/log?page&size | Occurrence log | |
| GET | {eId}/deliveries?effectiveasof&recordedasof | Production deliveries recorded against this item |
Notifications
Section titled “Notifications”Emits ProductionRecorded/ProductionRevoked for Orders to subscribe to. Listens for kanban’s WITHDRAW/REQUEST events to drive the detachWithdrawn reconciliation, and (Increment 5, default-off) a periodic law-probe runner that can self-heal via the same detachWithdrawn capability.
Schema notes
Section titled “Schema notes”demand_item.item_reference_record_idis reserved but unwritten. The would-be version pin is declared and read but never populated by either creation path (DemandServiceImpl.create()orDemandMembershipSagas.createWithCycle()).production_delivery.source_handleis never set. No writer populates it on any code path found — it’s an opaque reconciliation handle that no law reads.
Copyright: © Arda Systems 2025-2026, All rights reserved