SkillPilotAI

Case Study 1: Amazon / Flipkart E-commerce

Design an online store where tens of millions of people search products, fill carts, pay and track orders, without crashing even during a flash sale.

Read every case study in the standard interview format. The 8 steps are:

  1. Requirements: first ask what the system will do (functional) and how fast and available it must be (non-functional).
  2. Estimation: users, QPS, storage, bandwidth. The numbers tell you how big the system must be.
  3. API: which endpoints exist between client and server.
  4. Data model: which tables/collections, in which database, sharded on which key.
  5. High-level architecture: a diagram with users on top, then services, data and workers below.
  6. Flows: the step-by-step journey of the main request.
  7. Deep dives: the hardest problems and their solutions. This earns the most marks in an interview.
  8. Trade-offs: what you chose and why, and what you left out.

C.1 Requirements

Functional requirements:

  • Product catalog: a seller adds products; users see details, images, price and reviews.
  • Search and filter: by keyword, category, brand, price range and rating; autocomplete suggestions.
  • Cart: add/remove items, change quantity; the cart stays in sync across all devices after login.
  • Checkout: select address, apply coupon, pay (UPI, card, COD) and confirm the order.
  • Inventory: stock count per warehouse; reserve stock on order, release it back on cancel.
  • Flash sale: sell limited stock (for example 10,000 phones) at a fixed time among lakhs of users.
  • Order tracking: placed, packed, shipped, out for delivery, delivered statuses and notifications (SMS/email/push).

Non-functional requirements:

  • Availability 99.99% for browse and search; high availability for checkout too, but correctness comes first.
  • Latency: product page and search p99 < 200 ms; checkout p99 < 1 second.
  • Consistency: inventory and payment strongly consistent (no overselling); catalog and reviews can be eventually consistent.
  • Scale: 10 crore (100 million) DAU, normal peak 50K read QPS, flash-sale spike of 10 lakh+ (1 million+) requests/second.
  • Durability: no confirmed order or payment may ever be lost (replicated DB + Kafka).

C.2 Estimation (back-of-the-envelope)

ItemNumberHow it is derived
Daily active users10 croreAssumption: India-scale e-commerce, 2-3x during a festival sale.
Read QPS (browse/search)~25K avg, 75K peak10 crore users x 20 page views = 200 crore/day; / 86,400 = ~23K QPS; peak 3x.
Orders per day1 crore10% of users order; 1 crore / 86,400 = ~115 orders/sec on average, ~5K/sec at sale peak.
Order storage~7 TB/yearOne order is ~2 KB (items, address, amount); 1 crore x 2 KB = 20 GB/day; x 365 = ~7.3 TB/year, with 3x replication = ~22 TB.
Catalog size~5 TB metadata50 crore products x 10 KB metadata = 5 TB; images live separately in object storage (S3) + CDN.
Image bandwidth~100 Gbps avg23K page views/sec x ~500 KB images = ~11.5 GB/s = ~92 Gbps; the CDN serves 95%+ of this, so only ~5 Gbps reaches the origin.
Cache size (hot products)~200 GB RedisTop 2 crore hot products x 10 KB = 200 GB; a Redis cluster of 10 nodes x 32 GB.

C.3 API design

EndpointPurpose
GET /v1/products/{id}Product details, price, stock status (from cache)
GET /v1/search?q=phone&brand=x&page=1Search results from Elasticsearch, with filters and pagination
POST /v1/cart/itemsAdd an item to the cart {product_id, qty}
POST /v1/ordersPlace an order (with an Idempotency-Key header so no double order is created)
POST /v1/payments/callbackPayment gateway webhook; verify the signature, then confirm the order
GET /v1/orders/{id}/trackingCurrent order status and shipment events

C.4 Data model

Table / StoreFieldsWhy
products (MySQL/Postgres + Elasticsearch copy)product_id PK, seller_id, title, category, price, attributes JSON, updated_atSource of truth in SQL; synced to Elasticsearch via CDC for search.
inventory (MySQL, sharded by product_id)product_id, warehouse_id, available_qty, reserved_qty, versionStock needs strong consistency and row-level locking.
cart (Redis + DynamoDB/Cassandra)user_id PK, items list {product_id, qty, added_at}Fast reads/writes, key-value access pattern, lookup only by user_id.
orders (MySQL, sharded by user_id)order_id PK, user_id, status, amount, address_id, idempotency_key UNIQUE, created_atNeeds ACID transactions; a user's order history lands in a single shard.
payments (MySQL)payment_id PK, order_id, gateway_txn_id, status, amountRecord of money: strict consistency and audit.
shipment_events (Cassandra)order_id PK, event_time CK, status, locationAppend-heavy time-series data: fast writes and range reads by order_id.

C.5 High-level architecture

Amazon / Flipkart: architecture and request flow
  1. 1.Clients: Mobile App and Web Browser
  2. 2.Edge: DNS, CDN (images, JS/CSS, S3 origin) and WAF (bot / DDoS)
  3. 3.Load Balancer -> API Gateway (JWT auth, rate limit, routing)
  4. 4.Services: Catalog, Search, Cart, Order, Payment, Inventory, Notification, Tracking
  5. 5.Data: Redis Cluster (200 GB hot products, cart, stock), sharded MySQL (orders, inventory, payments), Elasticsearch (products index), Cassandra (shipment_events, cart copy), S3 (product images)
  6. 6.Outbox / CDC (Debezium) -> Kafka: OrderPlaced, PaymentSuccess, Shipped
  7. 7.Workers: Search Indexer (CDC -> ES update), Email/SMS Worker, Analytics (sales, funnels), Order Timeout (10 min -> CANCEL, stock release)

Checkout path: App -> WAF/LB -> Gateway -> Order -> Inventory reserve -> MySQL PENDING_PAYMENT -> Payment Gateway (Razorpay / PayU) -> webhook -> Kafka. Numbers: 10 crore DAU, 75K peak read QPS, flash sale 10 lakh+ rps, 1 crore orders/day.

  • Static images/JS come from the CDN (origin S3); API requests go WAF -> Load Balancer -> API Gateway (JWT + rate limit) to the microservices.
  • Read path: Catalog uses Redis cache-aside, MySQL on a miss; Search runs on Elasticsearch; Cart lives in Redis with a durable copy in Cassandra.
  • Checkout: Order Service checks idempotency, Inventory reserves with a 10 min TTL, and the order is saved as PENDING_PAYMENT + outbox in MySQL.
  • After the gateway webhook is verified, PaymentSuccess goes on Kafka; workers send SMS/email, update ES, and release stock on timeout.
ComponentWhat it does
CDNServes product images and JS/CSS static files from the edge; origin load drops by 90%.
API GatewayAuth (JWT), rate limiting per user/IP, routing to microservices.
Catalog ServiceServes product details; Redis cache-aside, reads MySQL on a miss.
Search ServiceFull-text search, filters and ranking on Elasticsearch; prefix index for autocomplete.
Cart ServiceKeeps the cart in Redis with a durable copy in DynamoDB/Cassandra; the guest cart is merged on login.
Inventory ServiceReserves/releases stock; uses a Redis atomic counter (Lua script) for flash sales.
Order ServiceCreates orders with an idempotency key and coordinates payment and inventory via a Saga.
Payment ServiceTalks to the gateway (Razorpay/PayU type), verifies webhooks, retries with idempotency.
KafkaCarries events like OrderPlaced, PaymentSuccess, Shipped; consumers are notification, search indexer, analytics.
Tracking ServiceTakes updates from courier partners, writes them to shipment_events and pushes to the user.

C.6 Main flows

Order placement flow (checkout):

  1. The user taps "Place Order"; the app sends POST /v1/orders with a unique Idempotency-Key (UUID).
  2. The API Gateway verifies the JWT, checks the rate limit and routes the request to Order Service.
  3. Order Service checks the idempotency key; if an order already exists it returns the same response.
  4. Order Service calls Inventory Service to reserve stock (reserved_qty += qty, 10 min TTL).
  5. The order row is saved in MySQL with status PENDING_PAYMENT, with an outbox event in the same transaction.
  6. The user is redirected via Payment Service to the gateway and pays by UPI/card.
  7. The gateway sends a webhook; Payment Service verifies the signature and publishes PaymentSuccess to Kafka.
  8. Order Service sets status CONFIRMED; Inventory converts reserved stock to sold.
  9. A notification worker reads the event from Kafka and sends SMS/email/push; a fulfilment event goes to the warehouse.
  10. If payment does not arrive within 10 min, the timeout worker sets the order to CANCELLED and releases the stock (compensating action).

Product search flow:

  1. The user types "iphone" in the search box; the autocomplete API is called every few keystrokes (debounce).
  2. The request reaches Search Service via CDN/LB; results of popular queries are cached in Redis for 1-5 min.
  3. On a cache miss, Search Service builds an Elasticsearch query: text match + filters + ranking (sales, rating).
  4. Elasticsearch shards search in parallel and the top results are merged.
  5. Search Service enriches the product IDs with price and stock from the Catalog/Inventory cache.
  6. The response carries product cards and image URLs (CDN); images load from the CDN.

Flash sale flow:

  1. Before the sale the stock count is loaded into Redis (for example sale:phoneX = 10000) and pages are pre-warmed on the CDN.
  2. When the sale starts, lakhs of users tap "Buy"; the API Gateway applies a per-user rate limit and bot detection.
  3. Requests go into a virtual waiting room/queue so traffic reaches the backend only at a controlled rate.
  4. Inventory Service does an atomic DECR through a Redis Lua script; if the result is >= 0 the user gets a token, otherwise sold out.
  5. The winners' order requests go onto Kafka; Order Service consumes at its own speed and creates the orders.
  6. If the user does not pay within the payment window (5-10 min), the Redis counter is INCR'd and the stock returns to the pool.

C.7 Deep dives

Checkout Saga
  1. 1.App: POST /orders + Idempotency-Key -> API Gateway: JWT + rate limit
  2. 2.Order Svc: key already seen? If duplicate, return the same response
  3. 3.Inventory: reserve qty (10 min TTL); Order: PENDING_PAYMENT + outbox
  4. 4.User redirected -> UPI / card pay -> gateway webhook ok
  5. 5.Payment: PaymentSuccess on Kafka -> Order CONFIRMED -> Inventory reserved to sold
  6. 6.SMS / email / push + warehouse fulfilment event
  7. 7.No payment in 10 min: Timeout Worker -> order CANCELLED + stock release (compensating action)

Order, Inventory and Payment are separate services, so the distributed transaction runs as a Saga.

Flash sale: 10,000 phones, 10 lakh buyers
  1. 1.Pre-load: sale:phoneX = 10000; pages pre-warmed on CDN
  2. 2.Buyers 10 lakh+ rps -> API Gateway: per-user limit + bot check
  3. 3.Waiting room: controlled rate
  4. 4.Redis Lua script, atomic: n = DECR sale:phoneX; n >= 0 gets a token, n < 0 sold out
  5. 5.Winner -> Kafka -> Order Svc consumes at its own speed
  6. 6.Did not pay: after 5-10 min INCR, stock goes back to the pool
Preventing double order / payment
  1. 1.App: req 1 key abc, req 2 key abc (double click / retry)
  2. 2.Order Svc: starts the Saga; orders in MySQL with idempotency_key UNIQUE
  3. 3.1st: INSERT creates the order; 2nd: duplicate, same response returned
  4. 4.The same key goes to the Payment Gateway too, so money is deducted only once

Problem: overselling in a flash sale (10,000 stock, 10 lakh buyers). If everyone hits a DB row lock the DB will die. So check-and-decrement is done with an atomic Lua script in Redis, and orders are then written to MySQL asynchronously via Kafka. The DB also keeps an available_qty >= 0 constraint as a safety net.

Problem: double order or double payment (retry, double click). The client sends an Idempotency-Key on every checkout; the orders table has a UNIQUE constraint. The same key is sent to the payment gateway, so retries are safe and money is not deducted twice.

Problem: distributed transaction across separate order, inventory and payment services. 2PC is slow, so use the Saga pattern. Every step has a compensating action: if payment fails, release stock and cancel the order. The outbox pattern keeps the DB write and the Kafka event consistent.

Problem: keeping the search index in sync with the catalog. CDC (Debezium) on the catalog DB sends changes to Kafka and the Search Indexer updates Elasticsearch. A lag of a few seconds is acceptable (eventual consistency).

Problem: cache stampede on a hot product. When a sale product's cache expires, thousands of requests must not hit the DB, so use request coalescing/locks, jittered TTLs, and keep hot keys in a local in-memory cache too.

Problem: payment gateway slow or down. Add a circuit breaker and keep multiple gateways; on failure, route to another gateway. For pending payments a reconciliation job polls the gateway for status.

C.8 Trade-offs

DecisionWhat was chosen and why
SQL vs NoSQL for ordersMySQL for orders and payments (ACID, sharded by user_id). NoSQL for cart and tracking events because they have simple key access and heavy writes.
Strong vs eventual consistencyStrong for inventory and payment; eventual for catalog, reviews, search. Making everything strong raises both latency and cost.
Stock reserve at add-to-cart vs at checkoutReserve at checkout; reserving in the cart lets people block stock and walk away. The cart only shows an "in stock" hint.
Sync vs async order processingOn a normal day the flow is sync; in a flash sale it is async through a Kafka queue and the user sees "order processing". Throughput rises, with a little delay.
Microservices vs monolithMicroservices at large scale so search and checkout can scale separately. For a small team, starting with a modular monolith is better.

C.9 Interview tips

  • Clarify scope first: not all of Amazon, focus on 2-3 core features (search, cart, checkout).
  • Treat the read-heavy (browse) and write-critical (checkout) paths separately; explain caching and consistency for each.
  • Always cover overselling and double payment: Redis atomic decrement, idempotency key, Saga.
  • Talk with numbers: QPS, storage, cache size; this convinces the interviewer the design is grounded.
  • Cover failure scenarios: payment gateway down, Kafka lag, cache failure, region outage.