RevRec brainstorm · Domain & architecture · Full design narrative

13 — Full design: RevRec Next (enterprise ASC 606 subledger)

Status: design lock candidate (2026-07-30)
Audience: engineers building or reviewing the system
Companions: 14 domain · 15 ASC 606 eng spec · 16 gap matrix · 17 AC boundary · 18 architecture from model · 19 exhaustiveness · ADRs

This is the wholesome design package: what we have, what differs, what we need, domain, architecture, tech choices, pipelines, NFRs, tradeoffs. Implementation sequencing is secondary; the model and architecture are not.


0. Executive picture

                    Zuora-class product outcomes
                    (ASC 606 subledger · close · journals · reports · audit)
                                      │
         Chargebee (primary) ──► SourceAdapter ──► other adapters (extendable)
                                      │
                         Extract (Temporal) → Raw lake
                                      │
                              Normalize → domain events
                                      │
                    ┌─────────────────┴─────────────────┐
                    ▼                                   ▼
             REVREC APPLY                          AR / facts
          Contract · PO · Schedule              Billing · Cash
          recognize(period)                     (same event log)
                    │                                   │
                    └──────────► amounts ───────────────┤
                                                        ▼
                                              ACCOUNTING CENTRAL
                                           journals · COA · close · export

Stance (locked): ASC 606 Contract/PO model first; architecture from domain properties (18); external UX shapes bend to the model (ADR 00010007).


1. Problem statement

Finance must recognize revenue under ASC 606 / IFRS 15 while billing continuously changes — upgrades, credits, usage, custom terms — and the GL must stay clean, closable, and auditable.

Product: an enterprise revenue subledger with Zuora-class outcomes: explicit contracts and performance obligations, recognition schedules, period close, journals, waterfalls/disclosures, and audit — Chargebee-native first, extendable sources, AR/AC as sibling contexts (00-product.md).

Correctness bar: ASC 606 + audit + close + journal integrity (ADR 0001). External report shapes adapt to the domain model — not the reverse.


2. What we have (inputs to this design)

Asset Role
Existing docs/0012 Product aims, domain drafts, arch, events, scenarios, Zuora tiers
ADRs 0001–0002 Stance + contracts≠invoices
ADRs 0003–0007 Domain/apply, stores/tech, policies, AC boundary, enterprise scope
Smoke scenario library (revanu/docs/revrec-scenarios) 317 behavior contracts — quarry for policies & accounting equality
AC / RevRec Premium PRD AC journals; does not calculate revenue
ASC 606 codification PDF Engineering mapped in 15
temporal_poc Extract-plane experiments

3. Have / diff / need

3.1 Inputs we already collected

Asset Use
Product aims, ASC 606 drafts, use cases, Zuora research Scope & domain
Smoke scenario library (317 cases) Policy / accounting-equality quarry
AC Premium PRD Journaling boundary
ASC 606 PDF Engineering paragraph map (doc 15)

3.2 Conceptual shift (domain)

From (common industry trap) To (this design)
Reconstruct promises from invoice time series Promises = Contract/PO from commercial arrangement
Spreadsheet/job output as truth Ops SoR + audit events as truth
Org-global batch as scale model Parallel apply by contract
Report shape defines the engine Engine defines amounts; reports project

3.3 Need (design must deliver)

  1. Explicit ASC 606 domain + apply API (14)
  2. Model-derived architecture (18)
  3. Owned extract + normalize to canonical events
  4. Close, journals interface, audit, RPO/waterfall
  5. AC boundary (17)
  6. Full capability coverage map (16)
  7. Migration via adapters + shadow accounting equality (when moving existing tenants)

4. Scope

4.1 In scope (product)

4.2 Out of scope (product)

4.3 Maturity labels (not a sprint plan)

Label Meaning
Foundation Single/multi contract ratable, bill/pay/credit, recognize, close, export, audit
Complete Multi-PO SSP, PIT/delivery, usage basic, ramps, strong disclosures
Enterprise-deep Multi-book, LT/ST, VC analyzer depth, cost amortization packs, advanced SOX UX
Out ERP-as-RevRec, sheet parity, Catalyst

Full matrix: 16.


5. End-user use cases (must solve)

From 10, treated as requirements:

ID User outcome
UC1 Onboard + backfill without spreadsheets
UC2 New subscription becomes an explainable contract/PO
UC3 Invoice/payment update AR/deferred without inventing revenue plans
UC4 Mid-term upgrade/downgrade with explicit mod treatment
UC5 Credit note with named policy
UC6 Cancellation with named terminate policy
UC7–9 Recognize, post journals, lock period
UC10 Audit: every dollar → source event + policy
UC11–12 Waterfall, SaaS metrics, RPO
UC13 Fix policy/late fact via replay/PPA — not archaeology
UC14 AR/cash views (Premium)

Smoke themes (modification, deferred-revenue, recognition-plan, credit-note, contract-term, multi-currency, service-delivery, ssp, ppa, subledger-ac, …) inform policies and golden tests, they do not define schema.


6. Architecture (technical depth)

Normative write-up: 18-architecture-from-model.mdmodel → properties → roles → tech.
Do not start from incumbent stacks; start from ASC 606 invariants.

6.1 Method (short)

Domain needs (single SoR, audit, per-contract parallelism, replay, rebuildable analytics, AC published facts) define component roles. Technologies only fill roles (ADR 0004).

6.2 Logical layers

Layer Responsibility Failure mode if wrong
SourceAdapter Pull/authenticate/page sources Lost/duplicate facts
Extract Schedule, checkpoint, write lake Unreplayable history
Normalize Map to canonical events Policy bugs look like source bugs
Apply Idempotent domain mutations Corrupt subledger
Close recognize / post / lock Unclosable books
Serve API/UI/read models Finance can’t operate
Analytics OLAP projections Slow/untrusted waterfalls
AC consumer Journals from published amounts Unbalanced GL

6.3 Runtime diagram

Source systems (Chargebee first; adapters later)
      │
      ▼
[Orchestrator: Extract] ──writes──► [Object lake: raw/{tenant}/{stream}/{run}/]
      │                                         │
      │                                    [Normalize]
      │                                         ▼
      │                              Canonical domain events
      │                                         │
      │                                         ▼
      │                         [Apply per contract key]
      │                                         │
      │                          ┌──────────────┴──────────────┐
      │                          ▼                             ▼
      │                   [Relational Ops SoR]          [applied_events]
      │                    contracts, POs,              idempotency+audit
      │                    schedules, billing…
      │                          │
      ├──── Close workflows ─────┤── recognize / post_journals / lock
      │                          │
      │                          ├──► outbox ──► [Analytics OLAP]
      │                          └──► recognition facts ──► [AC]
      ▼
[API / UI]

Defaults today: Temporal + Postgres + S3/GCS + ClickHouse + Python apply — substitutable within role.

6.4 Consistency & ordering

6.5 Orchestrator & SoR (why these properties)

Property needed Default filler Alternatives if properties held
Long backfill + per-contract concurrency + close visibility Temporal Other workflow engines with resume/visibility
Relational invariants on schedules/allocations/periods PostgreSQL NewSQL only if multi-region SoR is a hard requirement
Interactive waterfall/RPO ClickHouse BigQuery/Snowflake/Pinot
Immutable replay Object storage Any blob store with retention

Ops SoR is never the analytics engine; analytics is never the write SoR.

6.6 Analytics path

Outbox → async projector → OLAP tables keyed by tenant/period/contract. Rebuild from Ops SoR/events required. Waterfalls must reconcile to SoR within tolerance.

6.7 API surface (serve)


7. Pipelines & workflows

7.1 Steady-state sync (F2)

  1. ExtractWorkflow wakes on schedule / webhook hint.
  2. Pull stream pages → lake objects + cursor update.
  3. Normalize batch → domain events.
  4. Fan-out ApplyWorkflow by contract_key.
  5. Each apply commits or no-ops if duplicate event id.

7.2 Close cycle (F3)

  1. Preconditions: extract lag below threshold; exceptions queue empty or waived.
  2. recognize(period) for tenant (parallel by contract ranges).
  3. Control reports: deferred rollforward, accounting identity checks.
  4. post_journals(period) → JournalBatch + export object.
  5. Soft close → hard lock.
  6. Emit “RevRec closed” so AC can close revenue books.

7.3 Replay / correction (F7)

7.4 Onboarding backfill (F1)

Temporal backfill workflow pages historical CB data into lake, then apply in contract order. Progress and lag metrics are first-class.


8. Data stores (physical)

See 18 and ADR 0004.

Store Role SoR?
Raw lake (object store) Verbatim source payloads Source truth
Cursors Extract bookmarks Meta
Ops DB (relational) Subledger state Yes
Applied events Idempotency + audit Audit
Analytics OLAP Waterfalls/metrics No — rebuildable
Export objects Journal/partner files Files

9. Domain model (summary)

Full detail: 14-domain-model.md.

Core: Customer, Contract, PerformanceObligation, Allocation, RecognitionScheduleLine, Modification, AppliedEvent, Period.
Billing: BillingDocument, BillingLine, CashApplication.
Close: JournalBatch (RevRec-side summary; AC may own richer JE model).
Balances: deferred (contract liability), unbilled (contract asset), AR, recognized-to-date — reconcilable to events.

Apply API: open_contract, amend_contract, record_billing, record_payment, record_credit, record_delivery, recognize, post_journals, terminate.


10. ASC 606 mapping

Engineering rules + codification pointers: 15-asc606-engineering-spec.md.
Five steps are mandatory; everything else is policy/implementation.


11. RevRec ↔ AC

17-revrec-ac-boundary.md + ADR 0006.

One line: RevRec decides how much and when for revenue/deferred/unbilled; AC turns those amounts (plus billing/cash/tax events) into balanced journals and GL close.


12. NFRs (enterprise)

NFR Target design response
Multi-tenant isolation tenant_id everywhere; authz at API; optional RLS
Fat tenant Parallel apply by contract; partitioned data; no org mutex
Throughput Horizontal Temporal workers + Postgres read replicas for serve
Close time Incremental recognize; only open lines for period
Durability Lake + DB backups; PITR on Postgres
Audit applied_events immutable; every balance change cites event+policy
Replay Rebuild tenant from lake in shadow environment
Observability Workflow IDs, lag metrics, apply error queue, close dashboards
Security Secrets in vault; least-privilege CB keys; encryption at rest
DR Multi-AZ DB; lake cross-region replication

Exact numeric SLOs to be filled when production metrics from fat tenants are pulled (explicitly deferred measurement, not deferred architecture).


13. Tradeoffs

Choice Gain Cost
Domain-first write model Correctness, clarity Compat projections if old UX required
Daily schedule grain Amendment precision More rows — mitigate with rollups
Workflow orchestrator Operability of backfill/close Ops skill for the chosen engine
Relational Ops SoR Invariants Shard story for ultra-fat tenants later
Separate analytics OLAP Fast reports Reconciliation discipline
Prospective default mods Predictable SaaS Must still support cumulative when required
Unify-but-separate AC Clean boundaries Two close knobs to teach users

14. Migration strategy (design-level)

When moving existing tenants onto this engine:

  1. Shadow: feed the same commercial facts through Next; compare accounting equality (amounts by period/contract).
  2. Compat reads (optional): project familiar DTOs from Contract/PO if UX must stay frozen briefly.
  3. Cut over close so Next is SoR for recognition.
  4. AC consumes published recognition facts.
  5. Adapters for non-CB sources emit the same canonical events.

Curated smoke cases become early shadow fixtures (theme list in doc 16).


15. Risks

Risk Mitigation
Under-specified CB edge events Event catalog + lake retention + quarantine queue
Policy explosion Named policy registry (11); no spreadsheet branches
Dual-run drift Accounting equality jobs; freeze sheet parity debates
Fat-tenant Postgres limits Partition plan + apply sharding by contract hash
AC/RevRec close deadlocks Explicit ordering ADR 0006
Scope greed Maturity labels; model complete, delivery sliced

16. Open measurements (not open architecture)


17. How to read / refine

  1. Skim this doc (13).
  2. Deep: 14, 15.
  3. Scope: 16.
  4. AC: 17.
  5. Locks: decisions/.
  6. Compiled HTML: site/index.html after python3 build-site.py && python3 build-index.py.

Refine section-by-section against the HTML site; do not expand implementation code from this pass.