Skip to main content

Problem Statement

Design a large-scale e-commerce platform like Amazon that handles:
  • Product catalog with millions of items
  • Shopping cart and checkout
  • Order processing and fulfillment
  • Search and recommendations
  • User reviews and ratings

Requirements Clarification

Functional Requirements

Core Features:
├── Product Catalog
│   ├── Browse products by category
│   ├── Search products
│   ├── View product details
│   └── Product availability

├── Shopping Cart
│   ├── Add/remove items
│   ├── Persist across sessions
│   └── Apply promotions/coupons

├── Checkout & Orders
│   ├── Multiple payment methods
│   ├── Order placement
│   ├── Order tracking
│   └── Returns/refunds

├── User Management
│   ├── Authentication
│   ├── Order history
│   └── Saved addresses/payment methods

└── Seller Platform
    ├── Product listing
    ├── Inventory management
    └── Order fulfillment

Non-Functional Requirements

Scale:
├── 100M products
├── 50M daily active users
├── 10M orders per day
├── 100K orders during peak (per minute)
└── 500K concurrent users

Performance:
├── Search latency < 200ms
├── Product page load < 500ms
├── Checkout < 1 second
└── 99.99% availability

Consistency:
├── Inventory: Strong consistency
├── Orders: Strong consistency
├── Reviews: Eventual consistency
└── Search: Eventual consistency (minutes)

Capacity Estimation

Traffic:
├── 50M DAU × 10 pages/user = 500M page views/day
├── 500M / 86,400 = 5,800 requests/second average
├── Peak: 10x = 58,000 requests/second

├── Search: 20% of traffic = 1,200 searches/second
├── Add to cart: 5% = 300/second
└── Checkout: 10M orders / 86,400 = 115 orders/second

Storage:
├── Products: 100M × 10KB = 1TB
├── Product images: 100M × 5 images × 500KB = 250TB
├── Orders: 10M/day × 365 × 5KB = 18TB/year
├── User data: 100M users × 10KB = 1TB
└── Reviews: 500M reviews × 2KB = 1TB

Bandwidth:
├── Page view: 500KB average
├── 500M × 500KB = 250TB/day egress
└── Peak: 58,000 × 500KB = 29GB/second

High-Level Architecture

┌─────────────────────────────────────────────────────────────────┐
│                    E-Commerce Platform                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────┐    ┌──────────────┐    ┌───────────────────────┐  │
│  │  CDN    │◄───│    Client    │───►│  API Gateway          │  │
│  │(Static) │    │ (Web/Mobile) │    │  + Load Balancer      │  │
│  └─────────┘    └──────────────┘    └───────────┬───────────┘  │
│                                                  │              │
│         ┌────────────────┬───────────────┬───────┴────────┐    │
│         │                │               │                │    │
│    ┌────▼────┐     ┌────▼────┐    ┌────▼────┐      ┌────▼────┐│
│    │ Product │     │  Cart   │    │  Order  │      │  User   ││
│    │ Service │     │ Service │    │ Service │      │ Service ││
│    └────┬────┘     └────┬────┘    └────┬────┘      └────┬────┘│
│         │               │              │                │     │
│    ┌────▼────┐     ┌────▼────┐    ┌────▼────┐      ┌────▼────┐│
│    │Catalog  │     │  Redis  │    │  Order  │      │  User   ││
│    │   DB    │     │ Cluster │    │   DB    │      │   DB    ││
│    └─────────┘     └─────────┘    └─────────┘      └─────────┘│
│                                                                 │
│  ┌───────────────────────────────────────────────────────────┐ │
│  │                  Supporting Services                       │ │
│  ├───────────────┬───────────────┬───────────────────────────┤ │
│  │   Search      │   Inventory   │    Payment                │ │
│  │   Service     │   Service     │    Service                │ │
│  │(Elasticsearch)│               │                           │ │
│  └───────────────┴───────────────┴───────────────────────────┘ │
│                                                                  │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                    Message Queue (Kafka)                   │  │
│  │  order.created │ inventory.updated │ payment.processed    │  │
│  └───────────────────────────────────────────────────────────┘  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Core Components

1. Product Catalog Service

┌─────────────────────────────────────────────────────────────┐
│                  Product Catalog Design                      │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Data Model:                                                │
│  ┌────────────────────────────────────────────┐             │
│  │ Product {                                   │             │
│  │   id: uuid,                                │             │
│  │   sku: string,                             │             │
│  │   name: string,                            │             │
│  │   description: text,                       │             │
│  │   category_ids: [uuid],                    │             │
│  │   brand_id: uuid,                          │             │
│  │   seller_id: uuid,                         │             │
│  │   price: decimal,                          │             │
│  │   images: [url],                           │             │
│  │   attributes: jsonb,                       │             │
│  │   variants: [{size, color, sku, price}],   │             │
│  │   rating: float,                           │             │
│  │   review_count: int,                       │             │
│  │   created_at: timestamp,                   │             │
│  │   updated_at: timestamp                    │             │
│  │ }                                          │             │
│  └────────────────────────────────────────────┘             │
│                                                              │
│  Database Choice:                                           │
│  • PostgreSQL for structured product data                   │
│  • Elasticsearch for search                                 │
│  • Redis for hot products cache                             │
│  • S3 for product images                                    │
│                                                              │
│  Read Optimization:                                         │
│  • Cache popular products (80/20 rule)                      │
│  • Pre-compute category pages                               │
│  • CDN for images                                           │
│                                                              │
└─────────────────────────────────────────────────────────────┘
from dataclasses import dataclass
from decimal import Decimal
from typing import List, Dict, Optional
from datetime import datetime
import asyncio

@dataclass
class Product:
    id: str
    sku: str
    name: str
    description: str
    price: Decimal
    category_ids: List[str]
    seller_id: str
    images: List[str]
    attributes: Dict
    variants: List[Dict]
    rating: float = 0.0
    review_count: int = 0


class ProductCatalogService:
    def __init__(self, db, cache, search_client):
        self.db = db
        self.cache = cache
        self.search = search_client
    
    async def get_product(self, product_id: str) -> Optional[Product]:
        """
        Get product with caching.
        
        Cache strategy:
        - Cache popular products for 5 minutes
        - Cache product details for 1 minute
        """
        # Try cache first
        cached = await self.cache.get(f"product:{product_id}")
        if cached:
            return Product(**cached)
        
        # Fetch from database
        product = await self.db.products.find_one({"id": product_id})
        if not product:
            return None
        
        # Cache for future requests
        await self.cache.set(
            f"product:{product_id}",
            product,
            ttl=60  # 1 minute
        )
        
        return Product(**product)
    
    async def search_products(
        self,
        query: str,
        category: str = None,
        filters: Dict = None,
        sort: str = "relevance",
        page: int = 1,
        page_size: int = 20
    ) -> Dict:
        """
        Search products using Elasticsearch.
        """
        search_body = {
            "query": {
                "bool": {
                    "must": [
                        {
                            "multi_match": {
                                "query": query,
                                "fields": [
                                    "name^3",
                                    "description",
                                    "brand^2"
                                ]
                            }
                        }
                    ],
                    "filter": []
                }
            },
            "from": (page - 1) * page_size,
            "size": page_size
        }
        
        # Add category filter
        if category:
            search_body["query"]["bool"]["filter"].append(
                {"term": {"category_ids": category}}
            )
        
        # Add attribute filters
        if filters:
            for key, value in filters.items():
                search_body["query"]["bool"]["filter"].append(
                    {"term": {f"attributes.{key}": value}}
                )
        
        # Add sorting
        if sort == "price_low":
            search_body["sort"] = [{"price": "asc"}]
        elif sort == "price_high":
            search_body["sort"] = [{"price": "desc"}]
        elif sort == "rating":
            search_body["sort"] = [{"rating": "desc"}]
        
        results = await self.search.search(
            index="products",
            body=search_body
        )
        
        return {
            "total": results["hits"]["total"]["value"],
            "products": [hit["_source"] for hit in results["hits"]["hits"]],
            "page": page,
            "page_size": page_size
        }
    
    async def get_category_products(
        self,
        category_id: str,
        page: int = 1
    ) -> Dict:
        """
        Get products for category page.
        Pre-computed for popular categories.
        """
        cache_key = f"category:{category_id}:page:{page}"
        
        # Try pre-computed cache
        cached = await self.cache.get(cache_key)
        if cached:
            return cached
        
        # Compute category page
        products = await self.db.products.find({
            "category_ids": category_id,
            "is_active": True
        }).sort("popularity", -1).skip((page - 1) * 20).limit(20)
        
        result = {
            "products": list(products),
            "page": page
        }
        
        # Cache popular category pages
        await self.cache.set(cache_key, result, ttl=300)  # 5 minutes
        
        return result

2. Inventory Management

┌─────────────────────────────────────────────────────────────┐
│                  Inventory Management                        │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Challenges:                                                │
│  • Strong consistency for stock counts                      │
│  • Handle concurrent purchases                              │
│  • Prevent overselling                                      │
│  • Multi-warehouse inventory                                │
│                                                              │
│  Reservation Pattern:                                       │
│  ┌─────────────────────────────────────────────┐            │
│  │  Available: 100                             │            │
│  │  Reserved: 15 (held for checkout)           │            │
│  │  Sold: 85                                   │            │
│  │                                             │            │
│  │  Purchasable = Available - Reserved         │            │
│  │             = 100 - 15 = 85                 │            │
│  └─────────────────────────────────────────────┘            │
│                                                              │
│  Reservation Flow:                                          │
│  1. Add to cart → No reservation                           │
│  2. Start checkout → Reserve inventory (5 min TTL)         │
│  3. Complete payment → Convert to sold                      │
│  4. Abandon checkout → Release reservation (TTL expires)   │
│                                                              │
└─────────────────────────────────────────────────────────────┘
from datetime import datetime, timedelta
from dataclasses import dataclass
from typing import Optional
import asyncio

@dataclass
class InventoryItem:
    sku: str
    warehouse_id: str
    available: int
    reserved: int
    
    @property
    def purchasable(self) -> int:
        return max(0, self.available - self.reserved)


class InventoryService:
    RESERVATION_TTL = timedelta(minutes=5)
    
    def __init__(self, db, cache, event_bus):
        self.db = db
        self.cache = cache
        self.event_bus = event_bus
    
    async def check_availability(
        self, 
        sku: str, 
        quantity: int,
        warehouse_id: str = None
    ) -> bool:
        """
        Check if item is available for purchase.
        """
        if warehouse_id:
            inventory = await self.db.inventory.find_one({
                "sku": sku,
                "warehouse_id": warehouse_id
            })
            return inventory and inventory["purchasable"] >= quantity
        
        # Aggregate across warehouses
        pipeline = [
            {"$match": {"sku": sku}},
            {"$group": {
                "_id": "$sku",
                "total_available": {"$sum": "$available"},
                "total_reserved": {"$sum": "$reserved"}
            }}
        ]
        result = await self.db.inventory.aggregate(pipeline).to_list(1)
        
        if not result:
            return False
        
        purchasable = result[0]["total_available"] - result[0]["total_reserved"]
        return purchasable >= quantity
    
    async def reserve_inventory(
        self,
        order_id: str,
        items: list  # [{sku, quantity, warehouse_id}]
    ) -> bool:
        """
        Reserve inventory for checkout.
        Uses optimistic locking with version check.
        """
        reservation_id = f"reservation:{order_id}"
        expiry = datetime.utcnow() + self.RESERVATION_TTL
        
        # Start transaction
        async with self.db.start_session() as session:
            async with session.start_transaction():
                reservations = []
                
                for item in items:
                    # Atomically decrement available, increment reserved
                    result = await self.db.inventory.find_one_and_update(
                        {
                            "sku": item["sku"],
                            "warehouse_id": item["warehouse_id"],
                            "$expr": {
                                "$gte": [
                                    {"$subtract": ["$available", "$reserved"]},
                                    item["quantity"]
                                ]
                            }
                        },
                        {
                            "$inc": {"reserved": item["quantity"]},
                            "$push": {
                                "reservations": {
                                    "id": reservation_id,
                                    "quantity": item["quantity"],
                                    "expires_at": expiry
                                }
                            }
                        },
                        session=session,
                        return_document=True
                    )
                    
                    if not result:
                        # Insufficient stock, rollback
                        raise InsufficientStockError(item["sku"])
                    
                    reservations.append({
                        "sku": item["sku"],
                        "warehouse_id": item["warehouse_id"],
                        "quantity": item["quantity"]
                    })
                
                # Store reservation details for cleanup
                await self.cache.set(
                    reservation_id,
                    {
                        "items": reservations,
                        "expires_at": expiry.isoformat()
                    },
                    ex=int(self.RESERVATION_TTL.total_seconds())
                )
        
        return True
    
    async def confirm_reservation(self, order_id: str) -> bool:
        """
        Convert reservation to sold.
        Called after successful payment.
        """
        reservation_id = f"reservation:{order_id}"
        reservation = await self.cache.get(reservation_id)
        
        if not reservation:
            raise ReservationExpiredError(order_id)
        
        async with self.db.start_session() as session:
            async with session.start_transaction():
                for item in reservation["items"]:
                    await self.db.inventory.update_one(
                        {
                            "sku": item["sku"],
                            "warehouse_id": item["warehouse_id"]
                        },
                        {
                            "$inc": {
                                "available": -item["quantity"],
                                "reserved": -item["quantity"]
                            },
                            "$pull": {
                                "reservations": {"id": reservation_id}
                            }
                        },
                        session=session
                    )
        
        await self.cache.delete(reservation_id)
        
        # Publish event for analytics
        await self.event_bus.publish(
            "inventory.sold",
            {"order_id": order_id, "items": reservation["items"]}
        )
        
        return True
    
    async def release_expired_reservations(self):
        """
        Background job to cleanup expired reservations.
        """
        now = datetime.utcnow()
        
        # Find and release expired reservations
        result = await self.db.inventory.update_many(
            {"reservations.expires_at": {"$lt": now}},
            {
                "$pull": {
                    "reservations": {"expires_at": {"$lt": now}}
                }
            }
        )
        
        # Recalculate reserved counts
        pipeline = [
            {"$match": {"_id": {"$in": result.modified_ids}}},
            {"$set": {
                "reserved": {"$sum": "$reservations.quantity"}
            }}
        ]
        await self.db.inventory.aggregate(pipeline)


class InsufficientStockError(Exception):
    pass

class ReservationExpiredError(Exception):
    pass

3. Order Processing

┌─────────────────────────────────────────────────────────────┐
│                    Order State Machine                       │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌─────────┐   ┌──────────┐   ┌───────────┐   ┌─────────┐  │
│  │ Created │──►│ Reserved │──►│  Paid     │──►│Confirmed│  │
│  └────┬────┘   └────┬─────┘   └─────┬─────┘   └────┬────┘  │
│       │             │               │              │        │
│       │             │               │              ▼        │
│       │             │               │         ┌─────────┐  │
│       │             │               │         │Processing│  │
│       │             │               │         └────┬────┘  │
│       │             │               │              │        │
│       │             │               │              ▼        │
│       │             │               │         ┌─────────┐  │
│       │             │               │         │ Shipped │  │
│       │             │               │         └────┬────┘  │
│       │             │               │              │        │
│       │             │               │              ▼        │
│       │             │               │         ┌─────────┐  │
│       │             │               │         │Delivered│  │
│       │             │               │         └─────────┘  │
│       │             │               │                       │
│       ▼             ▼               ▼                       │
│  ┌─────────────────────────────────────────┐               │
│  │              CANCELLED                   │               │
│  │  (releases inventory, refunds payment)   │               │
│  └─────────────────────────────────────────┘               │
│                                                              │
└─────────────────────────────────────────────────────────────┘

4. Search Architecture

┌─────────────────────────────────────────────────────────────┐
│                 Search Architecture                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Data Flow:                                                 │
│  ┌──────────┐    ┌──────────┐    ┌───────────────────┐     │
│  │ Product  │───►│  Event   │───►│  Search Indexer   │     │
│  │   DB     │    │   Bus    │    │                   │     │
│  └──────────┘    └──────────┘    └─────────┬─────────┘     │
│                                            │                │
│                                            ▼                │
│                                   ┌────────────────┐       │
│                                   │ Elasticsearch  │       │
│                                   │    Cluster     │       │
│                                   │                │       │
│                                   │ • 3 primary    │       │
│                                   │ • 1 replica    │       │
│                                   │ • 100M docs    │       │
│                                   └────────────────┘       │
│                                                              │
│  Search Features:                                           │
│  • Full-text search with relevance                         │
│  • Faceted navigation (filters)                            │
│  • Autocomplete suggestions                                │
│  • Spell correction ("Did you mean...")                    │
│  • Synonym expansion                                        │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Key Design Decisions

Database Strategy

┌─────────────────────────────────────────────────────────────┐
│                 Database Per Domain                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Products: PostgreSQL + Elasticsearch                       │
│  ├── PostgreSQL for source of truth                        │
│  ├── Elasticsearch for search                              │
│  └── Redis for hot product cache                           │
│                                                              │
│  Inventory: PostgreSQL (strong consistency)                │
│  ├── ACID transactions required                            │
│  ├── Optimistic locking for updates                        │
│  └── Read replicas for availability checks                 │
│                                                              │
│  Orders: PostgreSQL + Event Store                          │
│  ├── Relational model for queries                          │
│  ├── Event sourcing for audit trail                        │
│  └── Partitioned by order date                             │
│                                                              │
│  Shopping Cart: Redis                                       │
│  ├── Fast access                                            │
│  ├── Session-based persistence                             │
│  └── Persistent for logged-in users                        │
│                                                              │
│  Reviews: MongoDB                                           │
│  ├── Flexible schema for review content                    │
│  ├── Eventual consistency acceptable                       │
│  └── Aggregation for ratings                               │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Checkout Flow

┌─────────────────────────────────────────────────────────────┐
│                  Checkout Saga                               │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. Create Order (pending)                                  │
│     │                                                        │
│  2. Reserve Inventory ◄─── Compensate: Release inventory   │
│     │                                                        │
│  3. Verify Address ◄────── Compensate: None needed          │
│     │                                                        │
│  4. Calculate Shipping ◄── Compensate: None needed          │
│     │                                                        │
│  5. Apply Promotions ◄──── Compensate: None needed          │
│     │                                                        │
│  6. Process Payment ◄───── Compensate: Refund payment       │
│     │                                                        │
│  7. Confirm Inventory ◄─── Compensate: Restore inventory    │
│     │                                                        │
│  8. Send Confirmation                                       │
│     │                                                        │
│  ✓  Order Complete                                          │
│                                                              │
│  Failure at any step → Execute compensations in reverse    │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Performance Optimizations

1. Product Pages:
   • CDN for static assets
   • Edge caching for product details
   • Pre-computed related products
   • Lazy load reviews

2. Search:
   • Search results caching (1 min TTL)
   • Popular searches pre-computed
   • Autocomplete from trie structure
   • Facet counts cached

3. Cart:
   • Session-local cart (Redis)
   • Merge on login
   • Background price updates

4. Checkout:
   • Inventory pre-check before payment
   • Async order confirmation email
   • Background fraud detection

Interview Tips

Key Discussion Points:
  1. Inventory consistency: How to prevent overselling?
  2. Search relevance: How to rank products?
  3. Checkout reliability: What if payment fails?
  4. Flash sales: How to handle 100x traffic spike?
  5. Multi-region: How to expand globally?