RevRec brainstorm · Direction alignment · DA · Domain + apply
ADR 0003 — Domain-first Contract/PO model with apply seam
Date: 2026-07-30
Status: Accepted
Deciders: RevRec Next design session (grill lock)
Context
We need an enterprise, Zuora-class revenue subledger. Legacy revanu uses sales-order / OrderDetails reconstruction. External UX and smoke goldens still speak “sales order,” but that must not define accounting truth.
Decision
- Optimal ASC 606 model is the system of record:
Contract→PerformanceObligation→ allocation →RecognitionScheduleLine→ period recognition. - Chargebee subscription (+ contract_term when present) is the default Step-1 contract identity. Invoices never create POs.
- Default PO granularity: one PO per distinct subscription item / plan charge (series treated as one PO when ASC 606-10-25-14 series criteria apply — typical SaaS ratable).
- Apply seam is mandatory: canonical domain events → idempotent domain services → transactional ops state + append-only applied-event/audit log.
- External parity bends to the model: Sales-order / warehouse / existing UI shapes are projections or anti-corruption adapters, not the core schema.
Consequences
- Engine tests assert accounting equality on Contract/PO/schedule/balances.
- Smoke scenarios quarried into policies and golden amounts; sheet shapes are not the bar.
- Migration can keep familiar UX by projecting SO-shaped reads from Contract/PO state.
Alternatives rejected
- Sales-order-as-root (keeps accidental complexity).
- Invoice-reconstructed POs (ADR 0002).
- Full event-sourcing-only (replay always) as day-one SoR — optional later for specific aggregates; ops DB is SoR now.