Custom Ecommerce Architecture: An Architect's Decision Framework
If you are evaluating a custom ecommerce architecture, you have almost certainly already decided that Shopify, BigCommerce, or a Salesforce Commerce Cloud monolith can't bend the way your business needs to. That instinct is sometimes right and frequently expensive. A large share of ambitious ecommerce builds eventually replatform off their homegrown systems, and the usual cause is not that custom was the wrong call — it's that the architecture was decided implicitly, by whoever wrote the first checkout endpoint, instead of deliberately.
This guide is written for the person who has to own that decision: the enterprise architect, the staff engineer, the CTO who will be on the hook when Black Friday traffic hits an inventory service that can't tell the truth about stock. It skips the glossary of patterns you can get anywhere and goes straight to the judgment calls — when custom is actually justified, how to decompose the commerce domain so it survives contact with scale, the consistency traps that quietly kill projects, and what a realistic build looks like.
When a Custom Ecommerce Architecture Is Actually Justified
Start by being honest about the question. "Custom" is not a binary. There is a spectrum from fully managed SaaS to a ground-up build, and most successful enterprise stacks live somewhere in the middle — they extend or compose rather than reinvent the entire commerce engine.
A custom architecture earns its cost only when your differentiation lives in the parts of commerce that off-the-shelf platforms model badly. In practice that means one or more of these is true:
- Your pricing or sourcing logic is genuinely non-standard. Customer-specific contract pricing, multi-distributor sourcing, quote-to-order, tiered B2B catalogs with inheritance, or usage-based/subscription billing. Platforms model "list price minus a coupon" beautifully and contract pricing terribly.
- Your order lifecycle is not "add to cart, pay, ship." Split fulfillment, partial shipments, pre-orders against future inventory, backorder allocation, or regulated workflows (controlled goods, age verification, export compliance).
- You sell through channels the platform treats as second-class. A custom front end on a managed backend (headless) solves presentation. It does not solve a backend that can't represent your domain.
- The integration surface is the product. ERP, WMS, PIM, tax, fraud, and CRM systems that must stay consistent in near real time. Most B2B teams badly underestimate this integration work.
If none of those apply, a custom build is a liability disguised as control. The honest default for a team under roughly $10M GMV with 3–8 engineers is a well-structured modular monolith — on a managed or extensible platform where possible. The biggest cost in ecommerce was never the initial build; it's the perpetual maintenance to keep pace with payments, tax, and security change. Custom multiplies that bill.
The Four Patterns, Ranked by What They Actually Cost You
The industry vocabulary — monolith, headless, microservices, composable/MACH — describes coupling, not quality. None is "modern" and none is "legacy." Each trades operational simplicity for independent evolvability. Here is the trade matrix an architect should keep in front of them:
| Pattern | What's decoupled | Best when | Real cost |
|---|---|---|---|
| Modular monolith | Nothing deployed separately; modules separated by clear internal boundaries | Single team, evolving domain, <$10M GMV | Discipline required to keep modules from leaking into each other |
| Headless | Front end from commerce engine (via API) | You need bespoke storefronts/omnichannel but standard commerce logic | Two deploy pipelines; the backend monolith's limits are still your limits |
| Microservices | Catalog, cart, pricing, orders, inventory each deploy independently | Multiple teams, distinct scaling profiles per domain | Distributed-systems tax: eventual consistency, saga orchestration, observability |
| Composable / MACH | The whole stack assembled from best-of-breed SaaS services | You want to buy each capability and own only the orchestration | Integration sprawl; you own the seams between vendors |
The single most important sentence in any architecture review: do not buy distributed complexity you don't need. Microservices solve an organizational problem (independent teams shipping independently) far more than a technical one. If you have one team, splitting cart and order into separate services buys you network failures, distributed transactions, and three new dashboards in exchange for nothing. Start as a modular monolith with hard internal seams; extract a service only when a specific module has a different scaling curve, failure domain, or ownership team than the rest.
Decomposing the Commerce Domain (Where the Real Design Happens)
Whatever pattern you choose, the architecture's quality is decided by your bounded contexts, not your deployment topology. Get the boundaries right and a monolith stays maintainable for years; get them wrong and microservices just distribute the mess.
These are the contexts that matter in nearly every serious ecommerce build, and the boundary rules that keep them honest:
- Catalog & Product (PIM). Owns the product model, variants, categorization, and merchandising attributes. It should know nothing about price or stock. The classic mistake is bolting price and inventory onto the product record — they have completely different change frequencies and owners.
- Pricing & Promotions. A rules engine, not a column. Contract pricing, tiers, promotions, and tax interplay are the most underestimated area in the entire build. Model it as deterministic rules with explicit precedence; never let "what price does this customer pay?" be answered by scattered
ifstatements. - Inventory & Availability. The hardest consistency problem in commerce (see the next section). It owns available-to-promise, not just a count.
- Cart & Checkout. Short-lived, high-write, latency-sensitive. Often the first context to deserve its own scaling profile.
- Order Management (OMS). The system of record for what was promised. Orders are immutable financial facts plus a state machine (placed → allocated → fulfilled → settled). This is where split shipments, returns, and partial refunds live.
- Payments. Always an anti-corruption layer over a provider (Stripe, Adyen). Your domain speaks "authorize/capture/refund"; the provider's webhooks and idempotency quirks stay quarantined behind that boundary.
- Identity & Customer. Accounts, B2B org hierarchies, addresses, consent. For B2B, the org-and-role model here drives pricing and catalog visibility everywhere else.
A practical test for a good boundary: could you change this context's database schema without coordinating with another team? If catalog and inventory share tables, you don't have two contexts — you have one with a confusing name.
The Consistency Traps That Sink Custom Builds
Most postmortems on failed custom ecommerce architecture trace back to three specific consistency failures. Design for them up front.
The oversell trap. Inventory is the canonical hard problem because reads (browse) vastly outnumber writes (purchase), yet the writes must be correct under contention. Two customers buy the last unit in the same 50ms window. If your "decrement stock" is a naive read-modify-write, you oversell. The fix is to treat inventory as a reservation ledger, not a counter: reserve at add-to-cart or checkout with a TTL, confirm on payment capture, release on expiry. Use the database's concurrency primitives (atomic conditional updates, SELECT ... FOR UPDATE, or an append-only event log) — do not rely on application-level locks.
The dual-write trap. The moment you have two stores that must agree — say, an order in your OMS and an event published to a fulfillment service — a naive "write to DB, then publish to the queue" will eventually leave them inconsistent when the process dies between the two steps. This is the dual-write problem, and it is guaranteed to bite at scale. The standard remedy is the transactional outbox: write the event to an outbox table inside the same transaction as the order, then relay it asynchronously. Architects who skip this spend Q4 reconciling orders by hand.
The distributed-transaction trap. As soon as placing an order spans services (reserve inventory, capture payment, create fulfillment), you cannot wrap them in one ACID transaction. You need a saga: a sequence of local transactions with compensating actions (refund the capture if fulfillment can't be created). Sagas are correct but expensive to build and reason about — which is the strongest argument for staying a monolith until a real scaling or ownership pressure forces the split. Every service boundary you add is a transaction you must now choreograph.
A Realistic Build Path
If you've decided custom is justified, sequence the work so that each phase ships something shippable and defers complexity until the domain demands it.
- Model the domain before the database. Write the bounded contexts, their owned data, and the events that cross between them. ArchiMate or a simple context map is enough — the artifact matters less than forcing the boundary decisions early, in a review, rather than implicitly in code.
- Build a modular monolith with hard internal seams. One deployable, one database, but modules that communicate through interfaces and domain events, not shared tables. This preserves the option to extract services later at near-zero refactoring cost.
- Get inventory and orders right first. They are the system of record and the hardest to change later. Reservation ledger, outbox, immutable order facts. Everything else can iterate.
- Wrap every external system in an anti-corruption layer. Payments, tax, ERP, shipping. Their models will change and break; the ACL keeps that blast radius out of your domain.
- Extract services only on evidence. A specific module needs to scale separately, fail separately, or be owned by a separate team. That evidence — not a conference talk — is your trigger.
The non-negotiables across every phase: multi-tenant data isolation enforced at the query layer (never trust the application to remember the org_id filter), idempotent payment and webhook handlers, an audit trail on every order state transition, and observability that can answer "where is order X right now?" in one query.
Where Determinism Beats Hand-Coding
The uncomfortable truth about custom ecommerce architecture is that most of it is the same every time — the same bounded contexts, the same reservation ledger, the same outbox, the same anti-corruption layers — and the part that's genuinely yours is buried in pricing rules, order lifecycle, and integration seams. Teams burn quarters re-implementing that commodity layer by hand, and that's also where the consistency bugs live.
This is exactly the boundary Archiet is built around. You express the domain once as a formal model — entities, bounded contexts, the order state machine, the rules — and Archiet deterministically generates conforming, gate-checked scaffolding (Flask/Next.js, FastAPI, Django, NestJS, Laravel, go-chi, Java/Spring, Rails, .NET) with the multi-tenant isolation, migrations, and auth wiring scaffolded to the model. Same model in, same code out — so the commodity layer is reproducible and reviewable, and your team spends its judgment on the pricing engine and the fulfillment workflow that actually differentiate the business.
Whether you generate the scaffolding or hand-write it, the lesson holds: a custom ecommerce architecture succeeds when the boundaries are deliberate, the consistency traps are designed for before they're hit, and complexity is added only when the domain earns it — not when the architecture diagram looks impressive.