system-design

Design an E-Commerce System (Amazon / Noon / Daraz)

Difficulty: Tier 3 Asked at: Amazon, Noon, Daraz, most retail companies Time budget: 45–60 min

E-commerce is a system of systems: catalog, search, cart, inventory, orders, payments, fulfillment. The classic trap is trying to design all of it — you can’t in 45 minutes. The skill is scoping to the hard core: the inventory / checkout problem, where correctness under concurrency (don’t oversell the last item) meets scale (flash sales). This study shows how to decompose a huge domain and then go deep where it matters.

Prerequisites: Microservices, Transactions, Saga Pattern, Distributed Locking


1. Requirements

Functional:

Non-functional:

Out of scope (declare!): recommendations (separate), the payment processor internals (separate), search internals (typeahead). 🚨 Scoping is the first skill this question tests.


2. Decompose the domain

🚨 You cannot design “all of Amazon.” Break it into services and pick the hard one to go deep on:

flowchart LR
    Catalog[Catalog Service<br/>read-heavy] 
    Search[Search Service]
    Cart[Cart Service]
    Inventory[Inventory Service<br/>⭐ the hard core]
    Order[Order Service]
    Payment[Payment Service]
    Fulfill[Fulfillment Service]
    Cart --> Order --> Inventory
    Order --> Payment
    Order --> Fulfill

Each is independently scalable with its own store (catalog: read replicas + cache; inventory: strongly-consistent). State the map, then say: “The interesting core is inventory + checkout — let me go deep there.”


3. The hard core: inventory & checkout without overselling

🚨 The central correctness problem: 500 people try to buy the last 10 units of a flash-sale item at the same millisecond. You must sell exactly 10, not 11, not 0.

This needs strong consistency on the inventory count — the opposite of the eventually-consistent feeds elsewhere in this repo. Options:

The DB (relational, ACID) is the right tool for inventory — 🚨 money and stock want transactions, not eventual consistency.


4. Checkout as a distributed workflow (saga)

Placing an order touches inventory, payment, and fulfillment — separate services, so no single ACID transaction spans them. Use a saga:

1. Reserve inventory        → (fail: sold out, stop)
2. Authorize payment        → (fail: release inventory, stop)
3. Confirm order            → 
4. Capture payment          → (fail: compensate)
5. Trigger fulfillment

Each step commits locally; failures trigger compensating actions (release reserved stock, void a payment auth). The order reaches a consistent terminal state (placed or fully rolled back). 🚨 Idempotency everywhere — retried steps (e.g. a payment call) must not double-charge or double-reserve. (Idempotency)


5. Deep dives

5a. Catalog & product pages (the read-heavy side)

Product pages are read millions of times, change rarely → cache aggressively + CDN for images/static content; read replicas for the catalog DB. This half looks like every read-heavy design; contrast it explicitly with the transactional checkout half. Search is a separate inverted-index service.

5b. Cart

Carts are per-user, frequently updated, tolerant of eventual consistency and even loss (annoying, not catastrophic). Store in a fast KV store (Redis) with persistence; merge guest + logged-in carts. Not the hard part — say so.

5c. Flash sales / hotspot inventory

A single hot SKU (the sale item) is a hot key with massive write contention on its count. Mitigations:

5d. Order consistency & idempotency

An order-placement request may be retried (user double-clicks, network retry) → use an idempotency key so the same checkout doesn’t create two orders or two charges. Orders are durable, auditable records (source of truth for payment/fulfillment).

5e. Eventual consistency where it’s safe

Not everything needs strong consistency — order history, recommendations, analytics, “customers also bought” can be eventually consistent and updated via events (event-driven). 🚨 Reserve strong consistency for stock and money; use eventual consistency everywhere else — this selective rigor is the senior insight.


6. Bottlenecks & scaling further

  1. Catalog read load → cache + CDN + read replicas.
  2. Inventory contention (flash sale) → sharded counters / queued purchases / reservations.
  3. Cross-service order consistency → saga + compensations + idempotency.
  4. Order/payment correctness → ACID inventory, idempotency keys, durable orders.
  5. Spikes → autoscale stateless services, queue buffering, graceful degradation.

7. Trade-off summary

Concern Consistency Store Why
Catalog/browse Eventual Cache + replicas + CDN Read-heavy, rarely changes
Cart Eventual Redis (KV) Per-user, loss-tolerant
Inventory Strong ACID relational Must never oversell
Order/payment Strong + idempotent ACID + saga Money must be correct
Recommendations/history Eventual Event-driven Not correctness-critical

8. Follow-up questions

How do you prevent overselling the last item when 500 people buy it at once? By making the stock decrement a strongly-consistent atomic operation that the database serializes. The canonical approach is a conditional update — decrement the quantity only if it's still greater than zero (`UPDATE ... SET qty = qty - 1 WHERE sku = ? AND qty > 0`) — so the database processes the 500 concurrent attempts one at a time and exactly the first 10 succeed (rows affected = 1) while the rest see zero rows affected and are told "sold out." This works because inventory is kept in an ACID store where such conditional decrements are atomic and isolated, unlike an eventually-consistent store where concurrent reads could all see "10 left" and all proceed. For higher scale you add reservations (reserve on checkout with a TTL, confirm on payment) and may shard the counter to spread contention, but the core guarantee comes from serialized atomic decrements against a consistent store — money and stock are exactly where you *don't* want eventual consistency.
Checkout spans inventory, payment, and fulfillment. How do you keep it consistent? With a saga, because those services have separate databases and a single ACID transaction can't span them. The checkout is modeled as a sequence of local transactions — reserve inventory, authorize payment, confirm order, capture payment, trigger fulfillment — each committing in its own service and publishing an event. If any step fails, the saga runs compensating transactions to undo the completed steps: release the reserved inventory, void the payment authorization, cancel the order. This drives the order to a consistent terminal state, either fully placed or fully rolled back, without a distributed transaction. Crucially every step is idempotent and keyed by an idempotency token, so retries (from network failures or double-clicks) don't double-charge the customer, double-reserve stock, or create duplicate orders — which is essential because saga steps are retried on transient failure.
How do you avoid designing "all of Amazon" and running out of time? Scope explicitly and early. State that e-commerce is a system of independent services — catalog, search, cart, inventory, order, payment, fulfillment — and sketch that decomposition so the interviewer sees you grasp the whole, then declare which parts are out of scope (recommendations, the payment processor internals, search internals) and name the hard core you'll go deep on: inventory and checkout, where correctness under concurrency meets flash-sale scale. This demonstrates the judgment the question is really testing — recognizing that the interesting, differentiated problem is not the read-heavy catalog (which looks like every other cached read system) but the transactional, oversell-proof checkout. Spending your 45 minutes deep on that, having framed the rest, beats a shallow tour of every service.
Which parts should be strongly consistent and which can be eventual? Reserve strong consistency for the two things that must be correct — stock and money — and use eventual consistency everywhere it's safe, which is most of the system. Inventory counts must be strongly consistent so you never oversell, and order/payment state must be strongly consistent and idempotent so you never double- charge or lose an order; these live in ACID stores with transactions and sagas. Everything else tolerates staleness: the product catalog and browsing (cached, replicated), the cart (loss-tolerant, per-user), order history, recommendations, "customers also bought," and analytics can all be eventually consistent and updated asynchronously via events. This selective rigor — being strict exactly where correctness is non- negotiable and relaxed everywhere else for scale and availability — is the senior insight, because applying strong consistency everywhere would needlessly cap scale and availability, while applying eventual consistency to stock or money would be a correctness disaster.

9. What junior / mid / senior answers look like


Further reading