Phase 3 Integrated Practice
Putting Phase 3 Concepts Together—Building a Complete Microservices-Based E-commerce Backend.
1. What You'll Learn
- Designing a Multi-Layer Compose Project Structure
- Service Discovery and Load Balancing
- Data Persistence Strategies
- Multi-environment configuration management
- Health Checkups and Self-Healing
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
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
- Microservices Compose Architecture: Three-layer network isolation—Frontend → Backend → Data Stores
internal: truePreventing direct external access to the database/cache over the network- Load balancing using multiple API replicas and Nginx upstreams
depends_on: { condition: service_healthy }Ensure that dependencies are truly ready- Log Rotation
logging: { options: { max-size: "10m" } }to Prevent the Disk from Filling Up - Managing Differences in dev/prod Override Files Between Environments
📝 Exercises
- Basic Problem (Difficulty: ⭐): Design a multi-service architecture for a blog system (Nginx + API + DB + Cache), write a
docker-compose.ymlfile, and start the system. - Advanced Exercise (Difficulty: ⭐⭐): Configure the API with multiple replicas (replicas: 3) and Nginx load balancing, and verify that requests are distributed across different replicas.
- 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.



