---
# === IDENTITY ===
id: software/system-design/e-commerce-platform/2026
canonical_question: "How do I design a scalable e-commerce platform architecture?"
aliases:
  - "e-commerce system design architecture"
  - "scalable online store architecture"
  - "design an e-commerce backend"
  - "e-commerce microservices architecture"
  - "how to architect an e-commerce platform"
  - "e-commerce platform system design interview"
  - "online marketplace architecture design"
  - "shopping platform architecture patterns"
entity_type: software_reference
domain: software > system-design > e_commerce_platform
region: global
jurisdiction: global
temporal_scope: 2020-2026

# === VERIFICATION ===
last_verified: 2026-02-23
confidence: 0.91
freshness: quarterly
version: 1.0
first_published: 2026-02-23

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: null
  next_review: 2026-08-22
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "Never process or store raw credit card numbers in your own system — always use PCI DSS-compliant payment gateways (Stripe, Adyen, Braintree) with tokenized payment methods"
  - "Inventory must be reserved (soft lock) before payment processing to prevent overselling — use optimistic locking or distributed locks, never rely on application-level checks alone"
  - "Each bounded context (catalog, cart, order, payment, inventory) must own its own database — shared databases create distributed monoliths that are harder to scale than the original monolith"
  - "Shopping cart must survive server restarts and session expiry — persist carts server-side (Redis or database), never rely solely on client-side storage for cart state"
  - "All inter-service communication for order processing must be idempotent — network failures will cause retries, and duplicate order creation or double-charging is unacceptable"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "Building a simple storefront with <100 products and <1K daily orders"
    use_instead: "Use a managed platform (Shopify, WooCommerce) instead of custom architecture"
  - condition: "Need to decompose an existing monolithic e-commerce app"
    use_instead: "software/migrations/monolith-to-microservices/2026"
  - condition: "Looking for specific payment gateway integration code"
    use_instead: "Consult the payment provider's official SDK documentation (Stripe, PayPal, Adyen)"

# === AGENT HINTS ===
inputs_needed:
  - key: scale
    question: "What is your expected scale (concurrent users and daily orders)?"
    type: choice
    options: ["Small (<1K users, <100 orders/day)", "Medium (1K-50K users, 100-10K orders/day)", "Large (50K-500K users, 10K-100K orders/day)", "Very large (>500K users, >100K orders/day)"]
  - key: team_size
    question: "How many backend engineers will maintain this system?"
    type: choice
    options: ["Small (<5)", "Medium (5-20)", "Large (20-50)", "Very large (>50)"]
  - key: architecture_style
    question: "Do you prefer microservices or a modular monolith?"
    type: choice
    options: ["Microservices (independent deployment)", "Modular monolith (simpler ops)", "Undecided — help me choose"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/system-design/e-commerce-platform/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-02-23)"

# === RELATED UNITS ===
related_kos:
  depends_on:
    - id: "software/migrations/monolith-to-microservices/2026"
      label: "Monolith to Microservices Migration"
  related_to:
    - id: "software/system-design/api-gateway/2026"
      label: "API Gateway Design Patterns"
    - id: "software/system-design/cqrs-event-sourcing/2026"
      label: "CQRS and Event Sourcing"
    - id: "software/system-design/microservices-communication/2026"
      label: "Microservices Communication Patterns"
  alternative_to:
    - id: "software/system-design/marketplace-platform/2026"
      label: "Two-Sided Marketplace Architecture"

# === SOURCES (7 authoritative sources) ===
sources:
  - id: src1
    title: "Build a Scalable E-commerce Platform: System Design Overview"
    author: DZone
    url: https://dzone.com/articles/scalable-ecommerce-platform-system-design
    type: technical_blog
    published: 2024-09-15
    reliability: moderate_high
  - id: src2
    title: "Amazon System Design — E-Commerce System Design"
    author: CodeKarle
    url: https://www.codekarle.com/system-design/Amazon-system-design.html
    type: technical_blog
    published: 2024-01-10
    reliability: high
  - id: src3
    title: "Design E-Commerce Applications with Microservices Architecture"
    author: Mehmet Ozkaya
    url: https://medium.com/design-microservices-architecture-with-patterns/design-e-commerce-applications-with-microservices-architecture-c69e7f8222e7
    type: technical_blog
    published: 2024-03-20
    reliability: moderate_high
  - id: src4
    title: "Architecting a Highly Available Serverless, Microservices-Based E-commerce Site"
    author: AWS Architecture Blog
    url: https://aws.amazon.com/blogs/architecture/architecting-a-highly-available-serverless-microservices-based-ecommerce-site/
    type: official_docs
    published: 2023-11-15
    reliability: high
  - id: src5
    title: "Shopify's Modular Monolithic Architecture: A Deep Dive"
    author: Mehmet Ozkaya
    url: https://mehmetozkaya.medium.com/shopifys-modular-monolithic-architecture-a-deep-dive-%EF%B8%8F-a2f88c172797
    type: technical_blog
    published: 2024-06-12
    reliability: moderate_high
  - id: src6
    title: "E-commerce Architecture and System Design for E-commerce Websites"
    author: GeeksforGeeks
    url: https://www.geeksforgeeks.org/system-design/e-commerce-architecture-system-design-for-e-commerce-website/
    type: community_resource
    published: 2024-08-22
    reliability: moderate
  - id: src7
    title: "Microservices Architecture Best Practices for E-Commerce"
    author: microservicesbook.org
    url: https://microservicesbook.org/ch3-best-practices.html
    type: technical_blog
    published: 2024-02-10
    reliability: moderate_high
---

# Scalable E-Commerce Platform Architecture

## TL;DR

- **Bottom line**: A scalable e-commerce platform decomposes into 8-12 bounded-context services (catalog, cart, order, payment, inventory, user, search, notification) communicating via async events, each owning its database, behind an API gateway with CDN caching.
- **Key tool/command**: `docker-compose up` with separate containers per service, or Kubernetes with Helm charts for production-grade orchestration.
- **Watch out for**: Distributed transactions across services (especially inventory + payment) — use the Saga pattern with compensating transactions, never two-phase commit.
- **Works with**: Any cloud provider (AWS, GCP, Azure); language-agnostic services; PostgreSQL, MongoDB, Redis, Elasticsearch, Kafka/RabbitMQ.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Never process or store raw credit card numbers in your own system — always use PCI DSS-compliant payment gateways (Stripe, Adyen, Braintree) with tokenized payment methods
- Inventory must be reserved (soft lock) before payment processing to prevent overselling — use optimistic locking or distributed locks, never rely on application-level checks alone
- Each bounded context (catalog, cart, order, payment, inventory) must own its own database — shared databases create distributed monoliths that are harder to scale than the original monolith
- Shopping cart must survive server restarts and session expiry — persist carts server-side (Redis or database), never rely solely on client-side storage for cart state
- All inter-service communication for order processing must be idempotent — network failures will cause retries, and duplicate order creation or double-charging is unacceptable

## Quick Reference

| Component | Role | Technology Options | Scaling Strategy |
|---|---|---|---|
| API Gateway | Route requests, rate limit, auth, SSL termination | Kong, AWS API Gateway, NGINX, Envoy | Horizontal — stateless, add instances behind LB |
| Product Catalog Service | CRUD products, categories, attributes, pricing | Node.js/Python + PostgreSQL or MongoDB | Read replicas + CDN cache; write sharding by category |
| Search Service | Full-text search, faceted filtering, autocomplete | Elasticsearch, OpenSearch, Typesense | Horizontal sharding by index; read replicas |
| User/Auth Service | Registration, login, JWT/OAuth, profiles | Node.js/Go + PostgreSQL + Redis (sessions) | Horizontal — stateless with token-based auth |
| Cart Service | Add/remove items, persist cart state, price calc | Node.js/Python + Redis (primary) + PostgreSQL (backup) | Horizontal — partition by user ID in Redis Cluster |
| Order Service | Order creation, lifecycle management, history | Python/Java + PostgreSQL (ACID) | Shard by order ID; archive old orders to cold storage |
| Payment Service | Gateway integration, tokenization, refunds | Node.js/Go + PostgreSQL + external gateway (Stripe) | Horizontal — idempotency keys prevent duplicates |
| Inventory Service | Stock levels, reservations, warehouse sync | Go/Java + PostgreSQL + Redis (hot counts) | Optimistic locking; shard by SKU range |
| Notification Service | Email, SMS, push notifications, webhooks | Node.js/Python + queue consumer (SQS/RabbitMQ) | Scale consumers independently based on queue depth |
| Recommendation Service | Personalized suggestions, "also bought" | Python (ML) + Redis (feature store) + Spark | Precompute offline; serve from cache; scale reads |
| CDN / Edge | Static assets, image delivery, edge caching | CloudFront, Cloudflare, Fastly | Automatic — scales with traffic globally |
| Message Broker | Async inter-service events, order saga coordination | Kafka, RabbitMQ, AWS SQS/SNS | Kafka: add partitions; RabbitMQ: add consumers |
| Monitoring & Observability | Distributed tracing, metrics, alerting, logging | Datadog, Grafana+Prometheus, Jaeger, ELK Stack | Scale collectors; sample traces at high volume |

## Decision Tree

```
START
├── Expected daily orders < 100 and products < 1K?
│   ├── YES → Use managed platform (Shopify/WooCommerce) — custom architecture is overkill
│   └── NO ↓
├── Team size < 5 backend engineers?
│   ├── YES → Modular monolith (single deploy, domain modules, shared DB with schema separation)
│   └── NO ↓
├── < 1K concurrent users?
│   ├── YES → Modular monolith with clear domain boundaries, prepare for future extraction
│   └── NO ↓
├── 1K–50K concurrent users?
│   ├── YES → Extract high-load services first (search, catalog, cart) as microservices; keep order/payment as a core service
│   └── NO ↓
├── 50K–500K concurrent users?
│   ├── YES → Full microservices with Kafka event bus, database-per-service, Kubernetes orchestration
│   └── NO ↓
├── > 500K concurrent users?
│   ├── YES → Microservices + CQRS/Event Sourcing for order service, multi-region deployment, database sharding
│   └── NO ↓
└── DEFAULT → Start with modular monolith, extract services as bottlenecks emerge
```

## Step-by-Step Guide

### 1. Define bounded contexts and data ownership

Map your e-commerce domain into distinct bounded contexts using Domain-Driven Design (DDD). Each context becomes a service boundary with its own database. The critical contexts are: Product Catalog, Shopping Cart, Order Management, Payment, Inventory, User/Auth, Search, and Notifications. [src3]

```
Bounded Contexts:
┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│   Catalog    │  │    Cart      │  │    Order     │
│  (MongoDB)   │  │   (Redis)    │  │ (PostgreSQL) │
└──────┬───────┘  └──────┬───────┘  └──────┬───────┘
       │                 │                 │
       └────────── API Gateway ────────────┘
                         │
┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│   Payment    │  │  Inventory   │  │   Search     │
│ (PostgreSQL) │  │ (PostgreSQL) │  │(Elasticsearch│
└──────────────┘  └──────────────┘  └──────────────┘
```

**Verify**: Each service can be deployed and tested independently → no compile-time dependencies between services.

### 2. Design the API gateway and routing layer

Place an API gateway in front of all services to handle authentication, rate limiting, request routing, and SSL termination. Use path-based routing (`/api/products/*` to Catalog, `/api/cart/*` to Cart, etc.). The gateway should also aggregate responses for the product detail page (catalog + inventory + reviews). [src1]

```yaml
# Kong or AWS API Gateway route config (conceptual)
routes:
  - path: /api/v1/products
    service: catalog-service
    methods: [GET]
    plugins: [rate-limit, jwt-auth, response-cache]
  - path: /api/v1/cart
    service: cart-service
    methods: [GET, POST, PUT, DELETE]
    plugins: [rate-limit, jwt-auth]
  - path: /api/v1/orders
    service: order-service
    methods: [GET, POST]
    plugins: [rate-limit, jwt-auth]
  - path: /api/v1/checkout
    service: payment-service
    methods: [POST]
    plugins: [rate-limit, jwt-auth, idempotency]
```

**Verify**: `curl -H "Authorization: Bearer <token>" https://api.example.com/api/v1/products` → returns product list with `200 OK`.

### 3. Implement the product catalog with search indexing

The catalog service stores products in a primary database (PostgreSQL for relational data or MongoDB for flexible schemas) and syncs changes to Elasticsearch for full-text search. Use Change Data Capture (CDC) or event publishing to keep the search index in sync. [src6]

```python
# catalog_service/events.py — Publish product changes to message broker
import json
from kafka import KafkaProducer

producer = KafkaProducer(
    bootstrap_servers=["kafka:9092"],
    value_serializer=lambda v: json.dumps(v).encode("utf-8"),
)

def publish_product_event(event_type: str, product: dict):
    """Publish product create/update/delete events for search indexing."""
    producer.send(
        "product-events",
        value={"event": event_type, "product": product},
    )
    producer.flush()
```

**Verify**: Create a product via API → check Elasticsearch: `curl localhost:9200/products/_search?q=<product_name>` → product appears within 2 seconds.

### 4. Build the cart service with Redis persistence

Use Redis as the primary store for shopping carts — it provides sub-millisecond reads and built-in TTL for cart expiry. Back up cart data to PostgreSQL for carts older than 30 minutes so they survive Redis eviction. [src2]

```python
# cart_service/cart.py — Redis-backed cart with PostgreSQL fallback
import json
import redis

r = redis.Redis(host="redis", port=6379, db=0, decode_responses=True)
CART_TTL = 86400 * 7  # 7 days

def add_to_cart(user_id: str, product_id: str, quantity: int):
    """Add item to cart. Cart is a Redis hash keyed by user_id."""
    cart_key = f"cart:{user_id}"
    r.hset(cart_key, product_id, quantity)
    r.expire(cart_key, CART_TTL)

def get_cart(user_id: str) -> dict:
    """Retrieve full cart contents."""
    cart_key = f"cart:{user_id}"
    cart = r.hgetall(cart_key)
    return {pid: int(qty) for pid, qty in cart.items()}
```

**Verify**: `add_to_cart("user123", "SKU-001", 2)` then `get_cart("user123")` → `{"SKU-001": 2}`.

### 5. Implement checkout with the Saga pattern

The checkout flow spans multiple services (cart, inventory, payment, order) and cannot use a single database transaction. Use the Saga pattern with compensating transactions: reserve inventory, process payment, create order. If payment fails, release the inventory reservation. [src3]

```
Checkout Saga Flow:
1. Cart Service    → Validate cart items and prices
2. Inventory Svc   → Reserve stock (soft lock with TTL)
3. Payment Service → Charge customer via gateway
   ├── SUCCESS → 4. Order Service → Create order record
   │                5. Inventory Svc → Confirm reservation (hard deduct)
   │                6. Cart Service → Clear cart
   │                7. Notification → Send confirmation email
   └── FAILURE → Compensate:
                    - Inventory Svc → Release reservation
                    - Notification → Send failure notice
```

**Verify**: Place test order → inventory decremented, payment captured, order record exists, cart cleared. Simulate payment failure → inventory restored to original count.

### 6. Set up event-driven communication with Kafka

Use Apache Kafka (or RabbitMQ for simpler setups) as the central event bus. Services publish domain events (OrderCreated, PaymentProcessed, InventoryReserved) and other services subscribe to react. This decouples services and enables eventual consistency. [src2]

```python
# order_service/events.py — Consume payment events
from kafka import KafkaConsumer
import json

consumer = KafkaConsumer(
    "payment-events",
    bootstrap_servers=["kafka:9092"],
    group_id="order-service",
    value_deserializer=lambda m: json.loads(m.decode("utf-8")),
    auto_offset_reset="earliest",
    enable_auto_commit=False,
)

for message in consumer:
    event = message.value
    if event["type"] == "PaymentSucceeded":
        order_id = event["order_id"]
        # Create order record, mark as confirmed
        create_order(order_id, event["items"], event["total"])
        consumer.commit()
    elif event["type"] == "PaymentFailed":
        # Trigger inventory compensation
        release_inventory(event["order_id"], event["items"])
        consumer.commit()
```

**Verify**: Publish a `PaymentSucceeded` event to Kafka → order record appears in database within 5 seconds.

### 7. Deploy with container orchestration

Package each service as a Docker container and orchestrate with Kubernetes. Use Horizontal Pod Autoscalers (HPA) to scale based on CPU/memory or custom metrics (queue depth, request latency). [src4]

```yaml
# k8s/catalog-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: catalog-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: catalog-service
  template:
    metadata:
      labels:
        app: catalog-service
    spec:
      containers:
      - name: catalog
        image: ecommerce/catalog-service:1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: catalog-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: catalog-service
  minReplicas: 3
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
```

**Verify**: `kubectl get hpa` → shows catalog-hpa with current/target metrics. Under load: `kubectl get pods -l app=catalog-service` → pod count increases.

## Code Examples

### Python: Order Service with Saga Orchestrator

> Full script: [python-order-service-with-saga-orchestrator.py](scripts/python-order-service-with-saga-orchestrator.py) (38 lines)

```python
# order_service/saga.py — Checkout saga orchestrator
# Input:  Cart contents (user_id, items), payment method token
# Output: Order confirmation or compensated failure
import httpx
import uuid
# ... (see full script)
```

### Node.js: Inventory Service with Optimistic Locking

> Full script: [node-js-inventory-service-with-optimistic-locking.js](scripts/node-js-inventory-service-with-optimistic-locking.js) (39 lines)

```javascript
// inventory_service/reserve.js — Atomic inventory reservation
// Input:  saga_id, items [{sku, qty}]
// Output: reservation confirmation or rejection
const { Pool } = require("pg");  // pg@8.13
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
# ... (see full script)
```

## Anti-Patterns

### Wrong: Shared database across services

```
// BAD — All services read/write the same database
┌──────────┐  ┌──────────┐  ┌──────────┐
│  Catalog  │  │  Order   │  │ Inventory │
│  Service  │  │  Service │  │  Service  │
└────┬──────┘  └────┬─────┘  └────┬──────┘
     │              │              │
     └──────── Shared MySQL ───────┘
// Creates coupling: schema changes break all services,
// impossible to scale services independently,
// single point of failure
```

### Correct: Database-per-service with event sync

```
// GOOD — Each service owns its data, syncs via events
┌──────────┐  ┌──────────┐  ┌──────────┐
│  Catalog  │  │  Order   │  │ Inventory │
│  Service  │  │  Service │  │  Service  │
│ (MongoDB) │  │ (Postgres│  │ (Postgres)│
└────┬──────┘  └────┬─────┘  └────┬──────┘
     │              │              │
     └──────── Kafka Event Bus ────┘
// Each service has its own DB, communicates via events.
// Independent scaling, deployment, and schema evolution.
```

### Wrong: Synchronous checkout across services

```python
# BAD — Blocking HTTP calls chain during checkout
def checkout(cart):
    inventory = requests.post("/inventory/reserve", json=cart)  # blocks
    payment = requests.post("/payment/charge", json=cart)       # blocks
    order = requests.post("/orders/create", json=cart)           # blocks
    # If payment service is slow, entire checkout hangs.
    # If order service fails after payment, no compensation.
    return order
```

### Correct: Saga pattern with async compensation

```python
# GOOD — Saga orchestrator with compensation on failure
async def checkout_saga(cart, payment_token):
    saga_id = uuid.uuid4()
    try:
        await reserve_inventory(saga_id, cart.items)
        payment = await process_payment(saga_id, payment_token, cart.total)
        order = await create_order(saga_id, cart, payment.id)
        return {"status": "confirmed", "order_id": order.id}
    except PaymentFailedError:
        await release_inventory(saga_id)  # compensating transaction
        return {"status": "failed", "reason": "payment_declined"}
    except OrderCreationError:
        await refund_payment(saga_id)     # compensating transaction
        await release_inventory(saga_id)
        return {"status": "failed", "reason": "order_creation_failed"}
```

### Wrong: Client-side only cart storage

```javascript
// BAD — Cart only in browser localStorage
localStorage.setItem("cart", JSON.stringify(cartItems));
// Lost when user switches device, clears browser, or incognito.
// No server validation of prices — users can manipulate.
// Cart abandoned analytics impossible.
```

### Correct: Server-side cart with client cache

```javascript
// GOOD — Server-side cart (Redis) with client-side sync
async function addToCart(productId, qty) {
  // Server is source of truth — validates price, stock
  const res = await fetch("/api/cart", {
    method: "POST",
    body: JSON.stringify({ product_id: productId, qty }),
    headers: { "Authorization": `Bearer ${token}` },
  });
  const cart = await res.json();
  // Client cache for instant UI, server syncs cross-device
  sessionStorage.setItem("cart_cache", JSON.stringify(cart));
  return cart;
}
```

## Common Pitfalls

- **Overselling during flash sales**: Inventory checks pass at application level but concurrent requests create a race condition. Fix: Use `UPDATE ... WHERE stock >= qty` with row-level locking in PostgreSQL, or Redis `WATCH`/`MULTI` for atomic decrement. [src2]
- **Cart price drift**: Product prices change between when user adds to cart and checkout. Fix: Re-validate all prices at checkout time against the catalog service; show price change warnings to user. [src6]
- **Distributed transaction failures**: Using 2PC (two-phase commit) across microservices causes tight coupling and availability issues. Fix: Replace with Saga pattern using compensating transactions and idempotency keys. [src3]
- **Search index lag**: Products updated in the catalog don't appear in search results for minutes. Fix: Use CDC (Change Data Capture) with Debezium or publish domain events to a Kafka topic consumed by the search indexer with <2s latency. [src1]
- **Session stickiness dependency**: Relying on sticky sessions for cart state means losing carts on server failure. Fix: Store cart in Redis Cluster (not server memory), key by user ID or session token. [src2]
- **Payment webhook idempotency**: Payment gateway sends duplicate webhooks (Stripe retries up to 3 times). Fix: Store a processed webhook ID in the database and check before processing; use database unique constraints on payment_intent_id. [src7]
- **N+1 queries on product listing**: Loading a product list that fetches category, images, and reviews per product. Fix: Use batch loading (DataLoader pattern), materialized views, or denormalize into a read-optimized product view. [src6]
- **Missing circuit breakers**: One slow downstream service (e.g., payment gateway) cascades failures to all services. Fix: Implement circuit breakers (Hystrix, resilience4j, Polly) with fallback responses and retry budgets. [src4]

## Diagnostic Commands

```bash
# Check service health across all microservices
for svc in catalog cart order payment inventory search; do
  curl -s "http://${svc}-service:8080/health" | jq '.status'
done

# Monitor Kafka consumer lag (detect processing bottlenecks)
kafka-consumer-groups.sh --bootstrap-server kafka:9092 \
  --describe --group order-service

# Check PostgreSQL active connections and locks
psql -c "SELECT pid, state, query, wait_event_type FROM pg_stat_activity WHERE state != 'idle';"

# Redis memory and cart key count
redis-cli INFO memory | grep used_memory_human
redis-cli DBSIZE

# Elasticsearch cluster health and index status
curl -s localhost:9200/_cluster/health | jq '.status,.active_shards'
curl -s localhost:9200/products/_count | jq '.count'

# Kubernetes pod status and recent events
kubectl get pods -n ecommerce -o wide
kubectl get events -n ecommerce --sort-by='.lastTimestamp' | tail -20
```

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Building a custom e-commerce platform with >1K daily orders | Selling <100 products with simple needs | Shopify, WooCommerce, or BigCommerce |
| Team has 5+ backend engineers and DevOps capability | Solo developer or small team without Kubernetes experience | Modular monolith or managed platform |
| Need independent scaling of catalog, search, and checkout | All components have similar load patterns | Modular monolith with domain modules |
| Regulatory requirements demand service isolation (PCI scope reduction) | No compliance requirements and simple payment flow | Monolith with Stripe Checkout (minimal PCI scope) |
| Multi-region deployment required for <100ms latency globally | Single-region audience with acceptable latency | Single-region deployment with CDN |
| Flash sales or highly variable traffic patterns | Steady, predictable traffic with no spikes | Fixed-size deployment with load balancer |

## Important Caveats

- Microservices architecture adds significant operational complexity — distributed tracing, service mesh, and container orchestration are prerequisites, not nice-to-haves. Teams without this expertise should start with a modular monolith and extract services incrementally.
- Eventual consistency between services means users may see stale data briefly (e.g., inventory count, order status). Design UIs to communicate this: "Checking availability..." spinners, optimistic UI updates with server reconciliation.
- Shopify processes 20+ TB/minute on a modular monolith (Ruby on Rails with Packwerk for module boundaries and pod-based isolation). Do not default to microservices — many successful e-commerce platforms at significant scale use modular monoliths. [src5]
- Database-per-service means no JOINs across services. Cross-service queries require either API composition at the gateway level or materialized read models (CQRS). Budget extra development time for reporting and analytics that span multiple domains.
- Payment service architecture should change rarely — it has the highest compliance burden (PCI DSS). Isolate it behind a stable API contract and use feature flags for payment method additions rather than frequent deploys.

## Related Units

- [Monolith to Microservices Migration](/software/migrations/monolith-to-microservices/2026)
- [API Gateway Design Patterns](/software/system-design/api-gateway/2026)
- [CQRS and Event Sourcing](/software/system-design/cqrs-event-sourcing/2026)
- [Microservices Communication Patterns](/software/system-design/microservices-communication/2026)
