System design template

E-commerce platform architecture.

Four services with four completely different consistency requirements, which is the whole reason this is not one application.

Download for macOS
v0.1.33 · .dmg · Apple Silicon & Intel
Signed & notarized by Apple · opens without a Gatekeeper warning
sha256 698955a0187bc039f4c74f5d05a9f10fbb27376a45788a0a241d1326b73873c7
Download for Windows instead
$curl -fsSL https://lucidtrain.com/install.sh | sh
E-commerce platform architecture. 13 components across 5 tiers.
E-commerce platform architecture. Rendered by the same ELK layout engine the app runs: the agent emits components, tiers and edges, and the engine places them, so the boxes cannot overlap.

The components

Every row below is read from the graph that produced the diagram above, so the two cannot disagree.

ComponentTierWhy it is there
StorefrontClientSupporting component
CDNEdgeSupporting component
API gatewayEdgeSupporting component
CatalogApplicationSupporting component
SearchApplicationSupporting component
CartApplicationSession-scoped, tolerates loss
OrdersApplicationThe only service that writes order state
InventoryApplicationReserves stock, releases on timeout
PaymentsApplicationSupporting component
PostgresDataSupporting component
RedisDataSupporting component
Event busDataSupporting component
Payment providerExternalSupporting component

Design decisions worth arguing about

A diagram shows what was chosen. It does not show what it cost, and that is usually the part that matters in a review or an interview.

The cart can lose data and the order cannot

These two look similar and could not be more different. A cart is a convenience: losing one annoys a user briefly, so it can live in a cache with a TTL and no durability guarantees. An order is a commitment with money attached, so it needs a durable transactional store and a single writer. Recognising that the two have different requirements is what lets the cart be cheap.

Reserve inventory, do not decrement it

Decrementing stock at checkout means an abandoned payment permanently removes an item from sale. Reserving with a timeout and releasing on expiry keeps availability honest at the cost of a background worker and a window where stock appears unavailable but is not. The alternative, decrementing only on payment success, oversells whenever two people check out simultaneously, which is worse for exactly the products you most want to sell.

Search is a projection and will lag

Serving search from the transactional database does not survive real catalog sizes, so search gets its own index built from events. That index is eventually consistent by construction: a price change is live in the catalog before it is live in search. Everyone accepts this until it applies to stock status, and a product shown as available in search but out of stock on the page is a support ticket rather than an architecture debate.

Only one service writes orders

It is tempting to let payments and shipping update order state directly, since they know things about it. Doing so means order state has several writers with no shared transaction, and reconstructing why an order is in a given state becomes archaeology. Routing every change through the order service costs an extra hop and keeps the state machine in one place where it can be reasoned about.

How it changes with scale

Traffic is dominated by browsing, which is cacheable and largely static, so the read path scales with the CDN rather than with your servers. Checkout volume is orders of magnitude smaller but touches the expensive consistent paths. This asymmetry is why splitting these services pays off: the parts that need to scale are not the parts that need to be correct.

Where it breaks first

A flash sale, which inverts every assumption at once. Traffic concentrates on a single product, so caches do not help because it is one key; inventory reservation becomes a contended row; and the payment provider becomes a rate limit you do not control. Systems that are comfortable at high steady volume routinely fall over on a small number of items at once.

Draw this yourself

Open the Diagram tab and describe the system. The agent emits a semantic graph rather than coordinates, so you can edit the components and the layout re-solves instead of drifting.

shell
$ Diagram an e-commerce platform: catalog, search, cart and order services behind an API gateway, inventory reservation, payments, Postgres and Redis, and an event bus that reindexes search.

When the shape is right, Implement in code turns the canvas into a markdown specification, every component, every relationship and the notes, and starts a real turn in the Code tab with it.

FAQ

Questions about this design

Should this be microservices or a monolith?

The boundaries drawn here are real, they follow genuinely different consistency and scaling requirements. That does not mean they need separate deployments on day one. The same boundaries inside one application give you most of the design benefit without the operational cost, and they are what makes splitting later straightforward.

Where do product images live?

Object storage behind a CDN, never in the database. The catalog stores references. This is the single easiest way to keep the transactional store small and fast.

How do you keep the search index in sync?

By consuming catalog change events rather than periodically re-scanning the database. A full rebuild path still needs to exist for the times the index is wrong, and it needs to be routine enough that people are willing to run it.

Do you need a separate inventory service?

If stock is per-warehouse, reserved across a basket, or shared with physical stores, yes: the logic is genuinely its own thing. For a single-warehouse shop, a column and a constraint on the product row is honest and much simpler.

More templates