Account state is scattered across six stores. Three areas mutate overlapping subsets. No single component reads all six — the recompute engine is the first to attempt a unified view by reading all five subs-api tables.

StoreWhat It HoldsWho MutatesConsistency Risk
cf_prodAccount records, entitlements, product configA1 (accounts), Entitlements APISchema coupled to product models
billing_prodInvoices, charges, dunning state, schedulesA2 (invoices), A3 (charges)Financial ledger — audit-critical
subs_accountAccount flags, billing profileA1 (profiles), A3 (bad_debt)Two areas write different fields
subs_customerStripe ↔ CF customer mappingA1 (setup), A2 (checkout)Race condition on concurrent writes
subs_dunningDunning state per accountA3 onlyClean — single writer
StripePayment status, mandates, invoice stateExternal (Stripe owns)Eventual consistency via webhooks
6
stores
3
areas mutating state
691
disagreeing accounts

When handlers write subsets of state in isolation, accounts drift into inconsistent states. 691 accounts were found in disagreement — dunning state says one thing, subscription state says another, Stripe says a third. No single component reads all six stores, so no component can detect the drift.

Partial Writes

Each handler writes the subset it knows about. A dunning handler updates subs_dunning but doesn't check subs_account flags. An account handler updates profiles but doesn't check payment state.

No Unified Reader

No component reads all six stores. Drift accumulates silently until a customer reports a problem or an audit catches it.

Cross-Area Coordination

Shared tables (subs_account, subs_customer) are written by multiple areas. Changes require cross-team coordination that doesn't exist in the current architecture.

Gather All State Compute Correct Apply Diff Emit Events

The target architecture centralizes state resolution. One component gathers all signals, computes what the account state should be, applies the minimal diff, and emits domain events. The recompute engine is the first implementation of this pattern — reading all five subs-api tables to compute correct dunning state.

One Reader

A single component reads all relevant state. No partial views, no assumptions about what other components have done.

One Truth

The computed state is the correct state. If it differs from what's stored, the stored state is wrong — apply the diff.

One Diff

Minimal writes. Only change what's actually wrong. Emit events for downstream consumers to react to the corrected state.