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
Copy
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
Copy
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
Copy
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
Copy
┌─────────────────────────────────────────────────────────────────┐
│ 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
Copy
┌─────────────────────────────────────────────────────────────┐
│ 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 │
│ │
└─────────────────────────────────────────────────────────────┘
- Python
- JavaScript
Copy
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
Copy
class ProductCatalogService {
constructor(db, cache, searchClient) {
this.db = db;
this.cache = cache;
this.search = searchClient;
}
async getProduct(productId) {
// Try cache first
const cached = await this.cache.get(`product:${productId}`);
if (cached) {
return JSON.parse(cached);
}
// Fetch from database
const product = await this.db.collection('products')
.findOne({ id: productId });
if (!product) return null;
// Cache for future requests
await this.cache.set(
`product:${productId}`,
JSON.stringify(product),
'EX', 60
);
return product;
}
async searchProducts({
query,
category = null,
filters = {},
sort = 'relevance',
page = 1,
pageSize = 20
}) {
const searchBody = {
query: {
bool: {
must: [
{
multi_match: {
query,
fields: ['name^3', 'description', 'brand^2']
}
}
],
filter: []
}
},
from: (page - 1) * pageSize,
size: pageSize
};
if (category) {
searchBody.query.bool.filter.push(
{ term: { category_ids: category } }
);
}
for (const [key, value] of Object.entries(filters)) {
searchBody.query.bool.filter.push(
{ term: { [`attributes.${key}`]: value } }
);
}
const sortOptions = {
price_low: [{ price: 'asc' }],
price_high: [{ price: 'desc' }],
rating: [{ rating: 'desc' }]
};
if (sortOptions[sort]) {
searchBody.sort = sortOptions[sort];
}
const results = await this.search.search({
index: 'products',
body: searchBody
});
return {
total: results.body.hits.total.value,
products: results.body.hits.hits.map(hit => hit._source),
page,
pageSize
};
}
}
module.exports = { ProductCatalogService };
2. Inventory Management
Copy
┌─────────────────────────────────────────────────────────────┐
│ 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) │
│ │
└─────────────────────────────────────────────────────────────┘
- Python
- JavaScript
Copy
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
Copy
class InventoryService {
static RESERVATION_TTL_SECONDS = 300; // 5 minutes
constructor(db, cache, eventBus) {
this.db = db;
this.cache = cache;
this.eventBus = eventBus;
}
async checkAvailability(sku, quantity, warehouseId = null) {
if (warehouseId) {
const inventory = await this.db.collection('inventory')
.findOne({ sku, warehouse_id: warehouseId });
return inventory &&
(inventory.available - inventory.reserved) >= quantity;
}
const result = await this.db.collection('inventory')
.aggregate([
{ $match: { sku } },
{ $group: {
_id: '$sku',
totalAvailable: { $sum: '$available' },
totalReserved: { $sum: '$reserved' }
}}
]).toArray();
if (!result.length) return false;
const purchasable = result[0].totalAvailable - result[0].totalReserved;
return purchasable >= quantity;
}
async reserveInventory(orderId, items) {
const reservationId = `reservation:${orderId}`;
const expiry = new Date(Date.now() +
InventoryService.RESERVATION_TTL_SECONDS * 1000);
const session = this.db.startSession();
try {
await session.withTransaction(async () => {
const reservations = [];
for (const item of items) {
const result = await this.db.collection('inventory')
.findOneAndUpdate(
{
sku: item.sku,
warehouse_id: item.warehouseId,
$expr: {
$gte: [
{ $subtract: ['$available', '$reserved'] },
item.quantity
]
}
},
{
$inc: { reserved: item.quantity },
$push: {
reservations: {
id: reservationId,
quantity: item.quantity,
expiresAt: expiry
}
}
},
{ session, returnDocument: 'after' }
);
if (!result.value) {
throw new InsufficientStockError(item.sku);
}
reservations.push({
sku: item.sku,
warehouseId: item.warehouseId,
quantity: item.quantity
});
}
await this.cache.set(
reservationId,
JSON.stringify({ items: reservations, expiresAt: expiry }),
'EX',
InventoryService.RESERVATION_TTL_SECONDS
);
});
return true;
} finally {
await session.endSession();
}
}
async confirmReservation(orderId) {
const reservationId = `reservation:${orderId}`;
const reservationData = await this.cache.get(reservationId);
if (!reservationData) {
throw new ReservationExpiredError(orderId);
}
const reservation = JSON.parse(reservationData);
const session = this.db.startSession();
try {
await session.withTransaction(async () => {
for (const item of reservation.items) {
await this.db.collection('inventory').updateOne(
{
sku: item.sku,
warehouse_id: item.warehouseId
},
{
$inc: {
available: -item.quantity,
reserved: -item.quantity
},
$pull: {
reservations: { id: reservationId }
}
},
{ session }
);
}
});
await this.cache.del(reservationId);
await this.eventBus.publish('inventory.sold', {
orderId,
items: reservation.items
});
return true;
} finally {
await session.endSession();
}
}
}
class InsufficientStockError extends Error {}
class ReservationExpiredError extends Error {}
module.exports = { InventoryService };
3. Order Processing
Copy
┌─────────────────────────────────────────────────────────────┐
│ Order State Machine │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌──────────┐ ┌───────────┐ ┌─────────┐ │
│ │ Created │──►│ Reserved │──►│ Paid │──►│Confirmed│ │
│ └────┬────┘ └────┬─────┘ └─────┬─────┘ └────┬────┘ │
│ │ │ │ │ │
│ │ │ │ ▼ │
│ │ │ │ ┌─────────┐ │
│ │ │ │ │Processing│ │
│ │ │ │ └────┬────┘ │
│ │ │ │ │ │
│ │ │ │ ▼ │
│ │ │ │ ┌─────────┐ │
│ │ │ │ │ Shipped │ │
│ │ │ │ └────┬────┘ │
│ │ │ │ │ │
│ │ │ │ ▼ │
│ │ │ │ ┌─────────┐ │
│ │ │ │ │Delivered│ │
│ │ │ │ └─────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ CANCELLED │ │
│ │ (releases inventory, refunds payment) │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
4. Search Architecture
Copy
┌─────────────────────────────────────────────────────────────┐
│ 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
Copy
┌─────────────────────────────────────────────────────────────┐
│ 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
Copy
┌─────────────────────────────────────────────────────────────┐
│ 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
Copy
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:
- Inventory consistency: How to prevent overselling?
- Search relevance: How to rank products?
- Checkout reliability: What if payment fails?
- Flash sales: How to handle 100x traffic spike?
- Multi-region: How to expand globally?