E-commerce platform architecture.
Four services with four completely different consistency requirements, which is the whole reason this is not one application.
The components
Every row below is read from the graph that produced the diagram above, so the two cannot disagree.
| Component | Tier | Why it is there |
|---|---|---|
| Storefront | Client | Supporting component |
| CDN | Edge | Supporting component |
| API gateway | Edge | Supporting component |
| Catalog | Application | Supporting component |
| Search | Application | Supporting component |
| Cart | Application | Session-scoped, tolerates loss |
| Orders | Application | The only service that writes order state |
| Inventory | Application | Reserves stock, releases on timeout |
| Payments | Application | Supporting component |
| Postgres | Data | Supporting component |
| Redis | Data | Supporting component |
| Event bus | Data | Supporting component |
| Payment provider | External | Supporting 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.
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.
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
- Notification Service DesignThe architecture is straightforward. The reason this needs to be one service is that the rules about when not to send are the hard part, and they have to live somewhere every sender passes through.
- Distributed Rate Limiter DesignEvery design here is a trade between how accurate the limit is and how much latency you are willing to add to every single request to achieve it.
- Observability Stack ArchitectureThe architecture is mostly about cost control. Collecting everything is technically easy and financially ruinous, so the interesting decisions are all about what to throw away.
- Authentication Service DesignStateless tokens make verification free and revocation hard, and that single tradeoff explains most of the components in this diagram.
Last updated