Core Concepts
The Three Primitives
Section titled “The Three Primitives”Entity
Section titled “Entity”An Entity is anything with identity. Entities have:
- A unique ID
- A type (defined by your domain)
- Optional tenant scoping
- Lifecycle states
- Relationships to other entities
Key characteristic: Entities participate in economic activity. They accumulate Facts.
A Fact is an immutable record of something that happened. Facts have:
- A timestamp (when it happened)
- A type and subtype (what happened)
- Links to entities involved
- A reference to the Config version used (if applicable)
- Data payload (event-specific details)
Key characteristic: Facts are append-only. They’re never modified or deleted.
Config
Section titled “Config”A Config is a versioned setting that governs behavior. Configs have:
- A type (what kind of setting)
- A version (increments on change)
- Effective/superseded timestamps
- Settings payload
Key characteristic: When a Config changes, a new version is created. Facts reference the specific version used.
Ledger Pattern
Section titled “Ledger Pattern”Each Entity with economic activity has a ledger—an append-only log of Facts. The ledger is the source of truth.
Entity: account_123 Ledger (Facts): fact_001: deposit { amount: 100 } fact_002: withdrawal { amount: 20 } fact_003: deposit { amount: 50 }
Derived State: Balance: 130 (computed from Facts)Cached State
Section titled “Cached State”Cached state is derived from Facts for performance. It’s:
- Explicitly named (BudgetState, BalanceState, etc.)
- Reconcilable against Facts
- Disposable (can be rebuilt)
- Never authoritative (Facts win on conflict)
Example:
class Account extends EntityLedger { getBalance(): number { // Try cache first const cached = this.getCachedState<BalanceState>('BalanceState'); if (cached) return cached.balance;
// Rebuild from Facts return this.recomputeBalance(); }
private recomputeBalance(): number { const facts = this.getFacts({ type: ['deposit', 'withdrawal'] }); const balance = facts.reduce((sum, fact) => { // Fold Facts into balance }, 0);
// Cache result this.setCachedState('BalanceState', { balance }); return balance; }}Multi-Tenant Isolation
Section titled “Multi-Tenant Isolation”z0 enforces tenant isolation at every layer:
- Authentication: tenant_id extracted from API key
- Data isolation: All queries filter by tenant_id
- Storage isolation: Per-tenant Durable Objects
- Audit trail: All actions create Facts
tenant_id comes from authentication, never from request parameters.
Generic Schema
Section titled “Generic Schema”Entities use a generic schema with flexible index slots:
CREATE TABLE entities ( id TEXT PRIMARY KEY, type TEXT, data TEXT, -- Full JSON payload
-- Queryable fields (mapped via domain manifest) ix_s_1 TEXT, -- String slot 1 ix_s_2 TEXT, -- String slot 2 ix_n_1 REAL, -- Number slot 1 ...);Your domain manifest maps logical fields to slots:
{ entities: { account: { fields: { status: { storage: 'ix_s_1' }, // status → ix_s_1 name: { storage: 'ix_s_2' }, // name → ix_s_2 balance: { storage: 'ix_n_1' } // balance → ix_n_1 } } }}This enables:
- No schema migrations when adding new entity types
- Multiple domains coexisting
- Type-safe queries via domain-specific helpers
Immutability
Section titled “Immutability”Facts are immutable. To correct a mistake:
// DON'T: Update the factawait updateFact(factId, { amount: 60 }); // ❌ Violates immutability
// DO: Create a correction factawait appendFact({ type: 'correction', source_id: originalFactId, data: { previous_amount: 50, corrected_amount: 60, reason: 'Entry error' }});Config Versioning
Section titled “Config Versioning”Configs create new versions when changed:
// v1: Initial configConfig { id: 'cfg_pricing', version: 1, settings: { rate: 0.05 } }
// v2: Rate changedConfig { id: 'cfg_pricing', version: 2, settings: { rate: 0.06 } }
// Facts reference the version usedFact { type: 'charge', config_id: 'cfg_pricing', config_version: 1, // Used v1 rate (0.05) data: { amount: 5.00 }}This enables:
- Exact reproduction of historical calculations
- Audit trail of config changes
- No retroactive changes
Traceability
Section titled “Traceability”Every economic Fact traces back through the system:
charge → outcome → invocation → config (pricing) ↓ config (qualification)Invariants ensure complete chains:
∀ Fact(charge) → ∃ Fact(outcome) WHERE charge.source_id = outcome.id∀ Fact(charge) → config_id ≠ null AND config_version ≠ nullError Handling
Section titled “Error Handling”Errors are first-class:
try { await processPayment(amount);} catch (error) { // Record error as Fact await appendFact({ type: 'error', subtype: 'payment_failed', data: { error_code: error.code, error_message: error.message, amount, retry_attempt: 1 } });
throw error;}This enables:
- Economic impact of failures tracked
- Error rates queryable
- Audit trail of what went wrong