Domain ownership¶
Implemented
Source:
doc/architecture/Domain_Ownership_Map.md · packages/services/*
Service packages own their tables, their NATS subjects, and their state machines. No cross-package direct joins. Cross-domain data flows through NATS events or explicit API calls.
Ownership matrix¶
flowchart LR
classDef svc fill:#e8f5e9,stroke:#2e7d32
classDef tbl fill:#e3f2fd,stroke:#1565c0
classDef sub fill:#fff3e0,stroke:#e65100
classDef sm fill:#f3e5f5,stroke:#6a1b9a
%% auth
AUTH[auth]:::svc --> AUTH_T1[oidc_sessions]:::tbl
AUTH --> AUTH_T2[service_accounts]:::tbl
AUTH --> AUTH_T3[user_posix_identities]:::tbl
%% inventory
INV[inventory]:::svc --> INV_T1[sku_catalog]:::tbl
INV --> INV_T2[nodes]:::tbl
INV --> INV_T3[node_resource_slots]:::tbl
INV --> INV_T4[os_images]:::tbl
INV --> INV_T5[node_image_cache]:::tbl
%% provisioning
PROV[provisioning]:::svc --> PROV_T1[allocations]:::tbl
PROV --> PROV_T2[allocation_resource_claims]:::tbl
PROV --> PROV_T3[allocation_placements]:::tbl
PROV --> PROV_T4[node_tasks]:::tbl
PROV --> PROV_S1[provisioning.* subjects]:::sub
PROV --> PROV_SM[Allocation FSM]:::sm
%% billing
BILL[billing]:::svc --> BILL_T1[usage_records]:::tbl
BILL --> BILL_T2[ledger_entries — immutable]:::tbl
BILL --> BILL_T3[low_balance_events]:::tbl
BILL --> BILL_S1[billing.* subjects]:::sub
BILL --> BILL_SM[Billing FSM]:::sm
%% payments
PAY[payments]:::svc --> PAY_T1[payment_sessions]:::tbl
PAY --> PAY_T2[payment_webhook_events]:::tbl
PAY --> PAY_S1[payments.* subjects]:::sub
%% terminal
TERM[terminal]:::svc --> TERM_R[Redis: terminal_token:*]
TERM --> TERM_SM[Session FSM]:::sm
%% appruntime
APR[appruntime]:::svc --> APR_T1[app_instances]:::tbl
APR --> APR_T2[app_instance_members]:::tbl
APR --> APR_T3[app_instance_events]:::tbl
APR --> APR_T4[app_manifests]:::tbl
%% storage
STG[storage]:::svc --> STG_T1[storage_objects]:::tbl
STG --> STG_T2[storage_namespaces]:::tbl
%% admin
ADM[admin]:::svc --> ADM_T1[audit_logs — immutable]:::tbl
ADM --> ADM_T2[refund_records]:::tbl
%% releases
REL[releases]:::svc --> REL_T1[allocation_ssh_public_keys]:::tbl
%% notification
NOTIF[notification]:::svc --> NOTIF_R[Redis pub/sub]
Service summary table¶
| Service | Owns tables | Emits subjects | State machines |
|---|---|---|---|
auth |
oidc_sessions, service_accounts, user_posix_identities |
— | Session lifecycle |
inventory |
sku_catalog, nodes, node_resource_slots, os_images, node_image_cache |
— | Node lifecycle, slot lifecycle |
provisioning |
allocations, allocation_resource_claims, allocation_placements, node_tasks |
provisioning.requested, .active, .failed, .releasing.requested, .releasing.completed, .release_failed, .force_release_requested |
Allocation FSM |
billing |
usage_records, ledger_entries, low_balance_events |
billing.low_balance_warning, .auto_release_pending, .balance_depleted |
Billing FSM |
payments |
payment_sessions, payment_webhook_events, ledger credit rows |
payments.balance_credited |
— |
terminal |
(Redis) | — | Session FSM |
appruntime |
app_instances, app_instance_members, app_instance_events, app_manifests |
(app-runtime subjects) | App instance FSM |
storage |
storage_objects, storage_namespaces |
— | — |
admin |
audit_logs, refund_records |
— | — |
releases |
allocation_ssh_public_keys |
— | — |
notification |
(Redis pub/sub channels) | — | — |
maas |
(facade) | — | Bare-metal node FSM (proxies MAAS) |
Cross-domain data flow¶
Cross-domain queries are forbidden. To get data across domains:
flowchart LR
A[Service A] -->|HTTP call<br/>via package import| B[Service B's API]
A -->|subscribes to| NATS[(NATS subject)]
NATS -->|emitted by| B
A -->|sees consistent view in| RM[(Read-model cache<br/>packages/shared/readcache)]
Three legitimate paths:
- Direct Go call within
cmd/api(services are imported as packages). - NATS subscription (workers; never publish directly from handlers).
- Read-model cache (
packages/shared/readcache) for cross-domain UI reads — seeUI_Read_Model_Cache_Architecture_v1.md.
Authority for shared concepts¶
| Concept | Authority | Reason |
|---|---|---|
| Tenant / project hierarchy | auth + authz |
Identity is the root; everyone else consults |
| Allocation existence + state | provisioning |
All other domains derive from it |
| Money | billing + payments |
Immutable ledger; credits and debits never UPDATE |
| Audit | admin |
Centralized; every privileged mutation writes here |
| Node lifecycle | inventory |
Including slot map approval |
| Policy values | policy shared package + policy_values table |
Scoped resolution; no hardcoded constants |
What package boundaries do not mean¶
- They do not imply network boundaries today.
cmd/apiimports every service package directly. Phase-2 extraction is planned but not done;Inter_Service_Communication.mddocuments the eventual split. - They do not imply separate databases. One Postgres instance; per-service credentials enforce boundaries at the user/grant level.