Phase 3 Integrated Practice

Putting Phase 3 Concepts Together—Building a Complete Microservices-Based E-commerce Backend.

1. What You'll Learn


2. The Story Behind an E-commerce Platform’s Backend

(1) Pain Point: 6 services that must be started manually

Charlie needs to deploy six services for the e-commerce platform’s backend: a React frontend, an Nginx reverse proxy, a Go API, PostgreSQL, a Redis cache, and a RabbitMQ message queue. Manually using docker run requires remembering more than 30 parameters, and since the startup order is strict, the error rate is extremely high.

(2) A Solution for One-Click Composition

Charlie handled it all with a single docker-compose.yml file.

BASH
docker compose up -d

(3) Benefits: One-click startup for 6 services

Reduced from 30 minutes manually to docker compose up -d 30 seconds. New hires can get the entire backend up and running in 5 minutes.


3. Microservices Architecture Design

(1) Architecture Overview

100%
graph TB
    USER["Browser"] --> NGX["Nginx<br/>:80 Reverse Proxy"]
    NGX -->|"api/"| API1["Go API #1<br/>:8080"]
    NGX -->|"api/"| API2["Go API #2<br/>:8080"]
    NGX -->|"api/"| API3["Go API #3<br/>:8080"]
    NGX -->|"/"| REACT["React SPA<br/>Static Files"]
    API1 --> PG["PostgreSQL<br/>:5432"]
    API2 --> PG
    API3 --> PG
    API1 --> REDIS["Redis<br/>:6379"]
    API2 --> REDIS
    API3 --> MQ["RabbitMQ<br/>:5672"]

(2) List of Services

Service Image Port Network Dependencies
Nginx nginx:1.25-alpine 80→80 frontend, backend api (healthy)
React Self-hosted (multi-stage) - frontend -
Go API Self-hosted(Multi-stage) 8080 backend, db-net, cache-net postgres (healthy), redis
PostgreSQL postgres:15-alpine 5432 db-net -
Redis redis:7-alpine 6379 cache-net -
RabbitMQ rabbitmq:3-management 5672, 15672 backend -

4. Writing Compose Files

▶ Example: Writing a Complete compose File (Difficulty: ⭐⭐⭐)

YAML
# ============================================
# docker-compose.yml - E-commerce microservices
# ============================================

services:
  # ---------- Frontend ----------
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - react-build:/usr/share/nginx/html:ro
    depends_on:
      api:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - frontend
      - backend
    healthcheck:
      test: ["CMD", "nginx", "-t"]
      interval: 30s
      timeout: 5s

  react:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    volumes:
      - react-build:/app/build
    networks:
      - frontend

  # ---------- Backend ----------
  api:
    build:
      context: ./api
      dockerfile: Dockerfile
    environment:
      DATABASE_URL: postgresql://appuser:${DB_PASSWORD:-secret}@postgres:5432/${DB_NAME:-ecommerce}
      REDIS_URL: redis://redis:6379
      RABBITMQ_URL: amqp://guest:guest@rabbitmq:5672
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
    networks:
      - backend
      - db-net
      - cache-net
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
      interval: 15s
      timeout: 5s
      retries: 3
      start_period: 10s
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"

  # ---------- Data Stores ----------
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: ${DB_PASSWORD:-secret}
      POSTGRES_DB: ${DB_NAME:-ecommerce}
    volumes:
      - pg-data:/var/lib/postgresql/data
    restart: unless-stopped
    networks:
      - db-net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data
    restart: unless-stopped
    networks:
      - cache-net
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s

  rabbitmq:
    image: rabbitmq:3-management-alpine
    ports:
      - "15672:15672"   # Management UI
    environment:
      RABBITMQ_DEFAULT_USER: guest
      RABBITMQ_DEFAULT_PASS: guest
    volumes:
      - mq-data:/var/lib/rabbitmq
    restart: unless-stopped
    networks:
      - backend

# ---------- Volumes ----------
volumes:
  pg-data:
  redis-data:
  mq-data:
  react-build:

# ---------- Networks ----------
networks:
  frontend:
  backend:
  db-net:
    internal: true   # No external access
  cache-net:
    internal: true

(1) Nginx Configuration (Load Balancing)

TEXT
# nginx.conf
upstream api_backend {
    server api:8080;   # Docker DNS round-robin to 3 replicas
}

server {
    listen 80;
    server_name localhost;

    # Static files (React SPA)
    location / {
        root /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
    }

    # API proxy
    location /api/ {
        proxy_pass http://api_backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

5. Multi-Environment Configuration

▶ Example: Overriding in the development environment (Difficulty: ⭐⭐)

YAML
# docker-compose.dev.yml
services:
  api:
    build:
      context: ./api
      dockerfile: Dockerfile.dev
    volumes:
      - ./api/src:/app/src   # Hot-reload
    environment:
      GO_ENV: development
    deploy:
      replicas: 1            # Single replica for debugging

  # Dev-only: database admin UI
  adminer:
    image: adminer
    ports:
      - "8081:8080"
    profiles: ["dev"]
    networks:
      - db-net

6. Verify the Deployment

(1) Verification Checklist

Test Item Command Expected Result
All services running docker compose ps 6+ services up
Nginx Healthy curl http://localhost 200 OK
API Health curl http://localhost/api/health {"status":"ok"}
Number of API replicas docker compose ps api 3 containers
Redis Connection docker compose exec redis redis-cli ping PONG
PostgreSQL Connection docker compose exec postgres pg_isready accepting connections
Database Isolation Pinging Postgres from the Nginx container Failed (internal network)

7. Complete Example: One-Click Deployment of an E-commerce Platform

BASH
# ============================================
# Complete walkthrough: E-commerce microservices
# ============================================

# 1. Build and start all services
docker compose up -d --build

# 2. Verify all services are running
docker compose ps

# 3. Check health status
docker compose ps --format "table {{.Name}}\t{{.Status}}"

# 4. Test the API
curl -s http://localhost/api/health | python3 -m json.tool

# 5. Verify load balancing (multiple requests to different replicas)
for i in $(seq 1 6); do
  curl -s http://localhost/api/health | jq -r '.hostname'
done

# 6. Check database connectivity from API
docker compose exec api wget -qO- http://localhost:8080/health

# 7. Access RabbitMQ management UI
# Open http://localhost:15672 (guest/guest)

# 8. View logs
docker compose logs -f api

# 9. Scale API to 5 replicas
docker compose up -d --scale api=5

# 10. Clean up
docker compose down
docker compose down -v  # Also removes data volumes

❓ FAQ

Q Should network aliases or service names be used for inter-service communication?
A In Compose, it is recommended to use service names (such as postgres and redis). Compose automatically registers DNS records for each service, and the service name serves as the hostname. Network aliases are used in scenarios where the same service requires different names across different networks.
Q How is the dependency startup order ensured?
A depends_on + condition: service_healthy. The API will only start after the health check for postgres passes. Note: depends_on: [db] (default condition: service_started) only waits for the container to start; it does not wait for the service to be ready.
Q How does the multi-instance API handle load balancing?
A Docker’s built-in DNS round-robin + Nginx upstream. All three API containers are registered with the DNS name api, and Nginx’s upstream api_backend { server api:8080; } will round-robin the requests. For more complex load balancing strategies, you can use HAProxy.
Q How do I configure master-slave replication for a database?
A For a single-node Compose instance, you can use multiple PostgreSQL containers with master-slave replication configured. However, for production-grade master-slave replication, we recommend using a managed database (AWS RDS / cloud provider) or dedicated tools (Patroni/Stolon). The PostgreSQL instance in this lesson is a single-node instance.
Q How do I implement hot reloading?
A In the development environment, mount the source code using a volume (e.g., -v ./api/src:/app/src) and use a hot reload tool (air for Go, nodemon for Node, flask --debug for Python). In the production environment, do not mount the source code; instead, apply configuration changes via environment variables and restart using docker compose up -d.

📖 Summary


📝 Exercises

  1. Basic Problem (Difficulty: ⭐): Design a multi-service architecture for a blog system (Nginx + API + DB + Cache), write a docker-compose.yml file, and start the system.
  2. Advanced Exercise (Difficulty: ⭐⭐): Configure the API with multiple replicas (replicas: 3) and Nginx load balancing, and verify that requests are distributed across different replicas.
  3. Challenge (Difficulty: ⭐⭐⭐): Add two sets of override files for dev and prod (dev: add Adminer and hot reload; prod: add resource limits and log rotation), then start each environment to verify the differences.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏