Design an E-commerce System

Difficulty: Advanced

Question

Design an e-commerce platform like Amazon. Focus on product catalog, cart, checkout, and inventory management.

Answer

An e-commerce system combines product discovery, cart management, checkout flow, and order fulfillment.

Core services: 1. Product Catalog: Browse, search, filter products with variants and pricing 2. Cart Service: Add/remove items, calculate totals with discounts 3. Inventory Service: Track stock levels, reserve inventory during checkout 4. Order Service: Process checkout, manage order lifecycle 5. Payment Service: Handle payment processing 6. Search Service: Elasticsearch for product search with filters and facets

Key challenges: - Inventory consistency: Prevent overselling when two users buy the last item simultaneously - Cart abandonment: Handle reserved inventory that is never purchased - Flash sales: Sudden traffic spikes for limited inventory items

Code examples

Inventory Reservation Pattern

// Problem: Two users add the last item to cart simultaneously
// Solution: Reserve inventory during checkout, release on timeout

async function addToCart(userId, productId, quantity) {
  // 1. Check available stock (not reserved stock)
  const product = await db.products.findById(productId);
  const reserved = await redis.get(`reserved:${productId}`) || 0;
  const available = product.stock - parseInt(reserved);

  if (available < quantity) {
    throw new Error('Not enough stock');
  }

  // 2. Add to cart (no reservation yet)
  await db.cartItems.upsert({
    userId, productId, quantity
  });
}

async function checkout(userId) {
  const cartItems = await db.cartItems.findAll({ userId });

  // 1. Reserve inventory (atomic operation)
  for (const item of cartItems) {
    const reserved = await redis.incrby(
      `reserved:${item.productId}`, item.quantity
    );

    // Check if reservation exceeds stock
    const product = await db.products.findById(item.productId);
    if (reserved > product.stock) {
      // Roll back this reservation
      await redis.decrby(`reserved:${item.productId}`, item.quantity);
      throw new Error(`${product.name} is out of stock`);
    }
  }

  // 2. Set reservation expiry (15 minutes to complete payment)
  for (const item of cartItems) {
    await redis.expire(`reserved:${item.productId}`, 900);
  }

  // 3. Create order and process payment
  const order = await createOrder(userId, cartItems);
  return order;
}

// On successful payment: deduct actual stock
// On payment failure or timeout: release reservation

Inventory reservation prevents overselling. Redis atomic operations ensure concurrent checkouts cannot exceed available stock. Reservations expire after 15 minutes to prevent inventory lockup.

Order State Machine

// Order lifecycle state machine
const ORDER_TRANSITIONS = {
  PENDING:     ['CONFIRMED', 'CANCELLED'],
  CONFIRMED:   ['PROCESSING', 'CANCELLED'],
  PROCESSING:  ['SHIPPED', 'CANCELLED'],
  SHIPPED:     ['DELIVERED', 'RETURNED'],
  DELIVERED:   ['RETURNED', 'COMPLETED'],
  COMPLETED:   [],
  CANCELLED:   [],
  RETURNED:    ['REFUNDED'],
  REFUNDED:    []
};

class OrderService {
  async transitionOrder(orderId, newStatus) {
    const order = await db.orders.findById(orderId);
    const allowedTransitions = ORDER_TRANSITIONS[order.status];

    if (!allowedTransitions.includes(newStatus)) {
      throw new Error(
        `Cannot transition from ${order.status} to ${newStatus}`
      );
    }

    // Perform side effects based on transition
    switch (newStatus) {
      case 'CONFIRMED':
        await this.deductInventory(order);
        await this.chargePayment(order);
        break;
      case 'SHIPPED':
        await this.createShipment(order);
        await this.notifyCustomer(order, 'Your order has shipped!');
        break;
      case 'CANCELLED':
        await this.releaseInventory(order);
        await this.refundPayment(order);
        break;
      case 'DELIVERED':
        await this.notifyCustomer(order, 'Your order was delivered!');
        break;
    }

    // Update order status
    await db.orders.update(orderId, {
      status: newStatus,
      statusHistory: {
        push: { status: newStatus, timestamp: new Date() }
      }
    });
  }
}

A state machine enforces valid order transitions and triggers appropriate side effects (payment, shipping, notifications) at each step. The status history provides a full audit trail.

Product Search with Elasticsearch

// Elasticsearch index for products
// PUT /products
{
  "mappings": {
    "properties": {
      "name": { "type": "text", "analyzer": "standard" },
      "description": { "type": "text" },
      "category": { "type": "keyword" },
      "brand": { "type": "keyword" },
      "price": { "type": "float" },
      "rating": { "type": "float" },
      "inStock": { "type": "boolean" },
      "tags": { "type": "keyword" }
    }
  }
}

// Search with filters and facets
async function searchProducts(query, filters, page = 0) {
  const result = await elasticsearch.search({
    index: 'products',
    body: {
      query: {
        bool: {
          must: [
            { multi_match: { query, fields: ['name^3', 'description', 'tags'] } }
          ],
          filter: [
            filters.category && { term: { category: filters.category } },
            filters.minPrice && { range: { price: { gte: filters.minPrice } } },
            filters.maxPrice && { range: { price: { lte: filters.maxPrice } } },
            { term: { inStock: true } }
          ].filter(Boolean)
        }
      },
      aggs: {
        categories: { terms: { field: 'category', size: 20 } },
        brands: { terms: { field: 'brand', size: 20 } },
        price_ranges: {
          range: {
            field: 'price',
            ranges: [
              { to: 50 }, { from: 50, to: 200 },
              { from: 200, to: 500 }, { from: 500 }
            ]
          }
        }
      },
      from: page * 20,
      size: 20,
      sort: [{ _score: 'desc' }, { rating: 'desc' }]
    }
  });

  return {
    products: result.hits.hits.map(h => h._source),
    facets: result.aggregations,
    total: result.hits.total.value
  };
}

Elasticsearch powers fast full-text product search with filtering and faceted navigation (category counts, price ranges). The name field is boosted 3x to prioritize title matches.

Key points

Concepts covered

Inventory Management, Cart, Checkout, Order Processing, Product Catalog