System Design
Amazon / Flipkart — High-Level Design
A walkthrough of how I'd design an e-commerce platform like Amazon, Flipkart, or Meesho — from a monolith to microservices, a CDC-backed search index, S3/CDN images, Redis locks on the last unit, and Kafka after payment. Below is the design, step by step.
- 01
Requirements & API
Search, view a product, add to cart, checkout, pay, and track an order. Scale target is 10M MAU and ~10 orders/sec. Orders and payments need strong consistency; search and product views need high availability. Core entities are User, Product, Inventory, Order, and Payment — with APIs for search, product details, cart, checkout, payment, and order status.

- 02
Day 0 — Monolith
Every module lives on one server talking to one database: User, Search, Product, Cart, Checkout, Orders, Payments. Easy to ship. Tightly coupled, so you can only scale it vertically — and only for so long. That is the prompt to split into microservices.

- 03
Day N — Microservices
An API Gateway routes to User, Search, Product, Cart, Orders, Checkout, and Payments. Each owns its database: User DB, Product DB (shared with Search at this stage), Cart DB, Orders DB, Payments DB. Search still reading the product table will not stay cheap — that is the next problem.

- 04
Why Search Cannot Be SQL LIKE
GET /product/search returns a paginated product list. SELECT * FROM products WHERE name LIKE '%term%' does a full table scan. Postgres pg_trgm (trigram indexes) helps fuzzy substring search, but a dedicated engine — Elasticsearch or AWS OpenSearch — is what search is actually for.

- 05
Indexing Products — Dual Write vs CDC
Search reads Elasticsearch; Products writes Postgres. Two ways to keep the index fresh: the admin write path dual-writes to both stores (fragile, easy to drift), or Change Data Capture streams product-table changes into Elasticsearch. CDC is the one that scales.

- 06
CDC Pipeline
Every datastore already logs mutations: Postgres WAL, MySQL binlog, MongoDB change streams, DynamoDB Streams, Oracle redo. Debezium reads those logs, publishes to Kafka, and a consumer indexes into Elasticsearch. Product writes stay in the Product DB; Search never queries it.

- 07
Images — S3 + CDN
Product images live in S3; the Product DB stores the URLs (product_id, name, description, qty, images[]). A CDN like CloudFront sits in front so listing and detail pages do not pull bytes from origin. Search still follows CDC from Product DB → Elasticsearch.

- 08
Product Details & Cart
GET /products/{id} hits the Product DB (images via S3 URL + CDN). POST /cart/add {productId, quantity} writes user_id, cart_id, product_id, qty into Postgres. The Cart service calls Product internally for the latest price so a stale cart cannot checkout at yesterday's number.

- 09
Checkout — Inventory Check
Checkout asks Inventory (source of truth, indexed on product_id) if the SKU is in stock. If yes, Payments talks to Stripe / Razorpay and records success and failure in Payments DB. Inventory is not the Product catalog — it is the stock ledger checkout actually trusts.

- 10
Payment Confirmed → Kafka
User clicks checkout → Inventory says yes → pay. On payment-done, publish a Kafka topic. Three consumers fan out: Order consumer inserts into Orders DB, Inventory consumer decrements qty, Notification service sends email and SMS. The request path stays short; fulfillment is async.

- 11
End-to-End Checkout
Gateway → Checkout → Inventory yes/no → Payments → Stripe/Razorpay → payment-done → Kafka. Order, inventory, and notification consumers run in parallel. Search, Product, Cart, and User sit beside this path with their own stores; CDC keeps Elasticsearch honest.

- 12
Redis Lock — Last iPhone
One iPhone left, four users at checkout. A Redis distributed lock makes the inventory check + payment critical section run for one requester at a time. Without it, all four can read qty=1 and all four can pay. The lock is the difference between overselling and a single winner.

- 13
Inventory Cache
Inventory DB is the source of truth; a cache in front keeps availability reads from knocking it over. On write, invalidate or update the cached qty so the next check is not serving a ghost unit. Cache is a shield, not a second ledger.

- 14
Product Qty vs Inventory
After payment, Inventory DB decrements. The Product DB still shows quantity on the PDP, so CDC from Inventory → Product keeps that number in sync. Catalog qty is eventually consistent; the lock + Inventory DB are what prevent the double sell.

- 15
Putting It All Together
User hits CDN for images and the API Gateway for everything else. Search reads Elasticsearch (CDC from Product). Cart, Orders, User, Payments each own a DB. Checkout takes a Redis lock, asks Inventory (cached), charges via Stripe/Razorpay, then Kafka fans out order creation, stock decrement, and notifications.
