CASE STUDY — DIAGNOSIS
The billing platform understood deeply enough to name its structural flaw. The internal models mirror the payment provider's models. A second provider is shimmed. Every seam leaks.
The billing platform processes millions of subscription changes, invoice finalizations, and payment attempts every month across three functional areas. It works — but it works because one payment provider is dominant. The internal data models, the event contracts, the retry logic — all of it assumes that provider's shape. When a second provider enters the picture, it takes the wrong path at every seam. This isn't a bug. It's architecture.
The billing platform divides into three functional areas, each with its own relationship to the payment provider. Understanding these boundaries — and where they leak — is the foundation of the diagnosis.
Customer creation, payment method attachment, billing profiles. The blind surface — it stores references to the provider's objects without understanding them. Spans the customer dashboard, internal API gateway, and admin panel.
Plan changes, proration, pricing. The most complex area — driven by a multi-stage calculation pipeline. Commits state before payment, then attempts to roll back on failure.
Invoice finalization, payment attempts, dunning, entitlement enforcement. Event-driven handlers that assume the provider's webhook contract. No single handler reads all shared data stores.
The root cause isn't technical debt or missing features. It's a modeling decision made early: internal objects mirror the payment provider's objects. Subscriptions look like the provider's subscriptions. Invoices look like the provider's invoices. Payment methods carry the provider's type taxonomy. The provider isn't just a vendor — it's the schema.
Every internal model — subscription, invoice, payment method, customer — carries the provider's shape as its canonical form. The shared database tables model the provider's ontology. The integration packages don't encapsulate the provider — they propagate it. Domain logic speaks the provider's nouns, not billing nouns.
A second payment provider enters a world shaped for the first and takes the wrong path at every seam. Wrong dunning path. Wrong collection path. Not a provider-specific problem — an existence proof that the architecture can only serve one provider.
Three areas form a directed cycle — each depends on the next through a different integration mechanism. All three reach inward to the same shared mutable state.
The structural flaw produces measurable failures. These trace back to two root causes: no single component sees the full picture, and internal models mirror the provider's models.
| Failure Mode | Root Cause | Area |
|---|---|---|
| Daily rollbacks | Commit-before-pay ordering — mutations persisted before payment confirmed | Subscription Commerce |
| Bad debt from upgrades | Uncollectible invoices from failed rollbacks that left orphaned charges | Commerce → Billing |
| State disagreements | Handlers writing subsets of shared state, no component reads all stores | Automated Billing |
| Low invoice coverage | Each handler covers one event type — minority of event paths handled | Automated Billing |
| Escalating support volume | Customers hitting silent failures with no dashboard visibility | All areas |
The seam map above was generated by the platform's diagram tooling — the same SVG engine that renders all ten structural forms in the design system. The coupling cycle, the failure taxonomy, the area decomposition — each was authored as a YAML spec and rendered automatically. The tooling made the diagnosis legible.
The structural flaw isn't in any single component. It's in the modeling decision that shaped every component: internal objects mirror the provider's objects. Fix the models, and the failure modes disappear.