Introduction
"Database per Service" is the foundation pattern of Microservices. Without it, you don't have real microservices — only modules sharing the same database (distributed monolith). This article explains why, how to choose the right DB, and how to handle data sharing.

1. Why Database per Service?
1.1 Shared Database — The Road to Distributed Monolith
❌ Shared Database Anti-pattern:
┌────────┐ ┌────────┐ ┌────────┐
│User µS │ │Order µS│ │Payment │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
└──────────┼──────────┘
▼
┌─────────────────────┐
│ Shared PostgreSQL │
│ ├── users │ ← ai own schema này?
│ ├── orders │ ← coupling tại data layer
│ ├── payments │ ← thay đổi schema = break all
│ └── products │
└─────────────────────┘
Problem:
- Schema changes affect all services
- Cannot scale database independently
- Tight coupling — deployment must be at the same time
- You cannot use different databases for different use cases
1.2 Database per Service
✅ Database per Service:
┌────────┐ ┌────────┐ ┌────────┐
│User µS │ │Order µS│ │Cart µS │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│PostgreSQL│ │PostgreSQL│ │ Redis │
│(users) │ │(orders) │ │(carts) │
└─────────┘ └─────────┘ └────────┘
Mỗi service own data riêng.
Schema changes chỉ ảnh hưởng 1 service.
Có thể chọn DB phù hợp nhất cho use case.
2. Polyglot Persistence — Choose the right DB
2.1 Decision Matrix
| Use Case | Database | Why |
|---|---|---|
| User profiles, Orders | PostgreSQL | ACID, relational, mature |
| Product catalog | PostgreSQL + Elasticsearch | Relational + Full-text search |
| Shopping cart | Redis | Fast, ephemeral, TTL support |
| Session store | Redis | In-memory, fast expiry |
| Activity log, Events | MongoDB / Kafka | Schema-flexible, append-only |
| Recommendations | Neo4j / Redis | Graph relationships / Caching |
| Analytics/BI | ClickHouse / BigQuery | Columnar, fast aggregation |
2.2 E-Commerce Platform Database Design
┌─────────────────────────────────────────────┐
│ User Service → PostgreSQL │
│ users, addresses, preferences │
├─────────────────────────────────────────────┤
│ Product Service → PostgreSQL + Elasticsearch│
│ products, categories, reviews (PG) │
│ search index (ES) │
├─────────────────────────────────────────────┤
│ Cart Service → Redis │
│ cart:{userId} → JSON (items, quantities) │
│ TTL: 7 days (auto-expire abandoned carts) │
├─────────────────────────────────────────────┤
│ Order Service → PostgreSQL │
│ orders, order_items, order_status_history │
├─────────────────────────────────────────────┤
│ Payment Service → PostgreSQL │
│ transactions, refunds, payment_methods │
└─────────────────────────────────────────────┘
3. Data Sharing Patterns
When Service A needs data from Service B:
3.1 API Composition
Service A calls Service B's API when it needs data. Simple but creates runtime dependencies.
3.2 Event-Carried State Transfer
Service B publishes events containing data → Service A saves a local copy.
ProductService publishes: ProductUpdated {id, name, price, image}
OrderService subscribes → lưu product snapshot trong order_items
→ Không cần gọi ProductService khi hiển thị order history
3.3 CQRS (see details in Lesson 9)
Create read-optimized views from events — dedicated query service.
4. Schema Migration Strategy
Each service manages its own schema:
- Flyway / Liquibase (Java) or Prisma Migrate / Knex (Node.js)
- Migration scripts versioned in source code
- Backward-compatible changes: add column (nullable), add table
- Breaking changes: multi-phase migration (add new → migrate data → remove old)
Summary
- Database per Service is non-negotiable for real microservices
- Select DB according to use case (Polyglot Persistence)
- Data sharing via Events (preferred) or API calls
- Schema migration is the responsibility of each service team
Next article: Lesson 8: Saga Pattern & Distributed Transactions