
Introduction
Microservices are more than just "splitting the monolith". If divided the wrong way, you will have a "distributed monolith" — with all the disadvantages of both architectures. This article covers design principles to divide microservices properly.
1. What are microservices?
Microservices is an architectural style in which an application is composed of many small, independent services:
Monolith Microservices
┌──────────────────────┐ ┌──────────┐ ┌──────────┐
│ Một ứng dụng │ │ Order │ │ Payment │
│ ┌──────┐ ┌──────┐ │ │ Service │ │ Service │
│ │Order │ │Payment│ │ → └──────────┘ └──────────┘
│ ├──────┤ ├──────┤ │ ┌──────────┐ ┌──────────┐
│ │Invent│ │Notif │ │ │Inventory │ │ Notif │
│ └──────┘ └──────┘ │ │ Service │ │ Service │
│ Shared Database │ └──────────┘ └──────────┘
└──────────────────────┘ Mỗi service có DB riêng
Features
- Runs in a separate process: Each service is an independent process/container
- Network communication: HTTP/REST, gRPC, Message Queue
- Independent deployment: Deploy service A without affecting service B
- Private Database: Database per Service pattern
- Small team: 2-Pizza team (5-8 people) owning 1-3 services
2. Single Responsibility Principle (SRP)
Each service is only responsible for one operation and has one reason to change.
✅ Đúng — Mỗi service một nghiệp vụ rõ ràng:
├── OrderService → Quản lý vòng đời đơn hàng
├── PaymentService → Xử lý thanh toán
├── InventoryService → Quản lý tồn kho
├── NotificationService → Gửi email/SMS/push
└── UserService → Quản lý tài khoản
❌ Sai — Service "thùng rác":
├── OrderPaymentService → 2 nghiệp vụ gộp
├── CommonService → Mọi thứ chung
└── UtilityService → Không rõ trách nhiệm
Sign of SRP violation
- Service has too many unrelated API endpoints
- Changing feature A requires retesting feature B
- Many teams fix the same service
- Service name contains "and" or "util" or "common"
3. Domain-Driven Design (DDD)
3.1 Why is DDD needed?
DDD helps answer the most important question: "Where is the boundary of each service?"
Instead of dividing by technique (frontend service, backend service, database service), DDD divides by business domain.
3.2 Strategic Design
Ubiquitous Language
Build a common language between developers and domain experts:
Domain Expert (Business): Developer (Tech):
"Đơn hàng" → Order
"Thanh toán" → Payment
"Giao hàng" → Shipment
"Hoàn tiền" → Refund
"Kho hàng" → Inventory
Đảm bảo: Code, API, database, documentation đều dùng cùng thuật ngữ
Bounded Context
Bounded Context defines the scope within which a model has meaning:
┌──────────────────────────────────────────────────────┐
│ E-Commerce System │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ Order │ │ Catalog │ │ Customer │ │
│ │ Context │ │ Context │ │ Context │ │
│ │ │ │ │ │ │ │
│ │ "Product" │ │ "Product" │ │ "Customer" │ │
│ │ = OrderItem │ │ = SKU with │ │ = Account │ │
│ │ (id, qty, │ │ specs, │ │ with prefs, │ │
│ │ price) │ │ images, │ │ addresses │ │
│ │ │ │ pricing │ │ │ │
│ └─────────────┘ └─────────────┘ └──────────────┘ │
│ │
│ Cùng từ "Product" nhưng ý nghĩa KHÁC NHAU │
│ trong mỗi Bounded Context │
└──────────────────────────────────────────────────────┘
Important Rule: Each Bounded Context → One (or several) Microservices.
3.3 Context Mapping — Relationships between Contexts
┌─────────────┐ Published ┌──────────────┐
│ Order │────Language────▶│ Payment │
│ Context │ │ Context │
└──────┬──────┘ └──────────────┘
│
│ Customer/Supplier
▼
┌─────────────┐ Conformist ┌──────────────┐
│ Shipping │◀───────────────│ 3rd Party │
│ Context │ │ Logistics │
└─────────────┘ └──────────────┘
| Relationship | Description |
|---|---|
| Shared Kernel | 2 contexts share subset code/model |
| Customer/Supplier | Upstream provides API, downstream consumes |
| Conformist | Downstream fully complies with the upstream model |
| Anti-Corruption Layer | Translation layer between foreign model and internal model |
| Published Language | Communicate via standard format (JSON schema, Protobuf) |
3.4 Tactical Design — Building Blocks
Bounded Context
│
├── Entity → Có identity, lifecycle (Order, User)
├── Value Object → Không có identity, immutable (Money, Address)
├── Aggregate → Cluster of entities, consistency boundary
│ └── Aggregate Root → Entry point (Order là root, OrderItem là child)
├── Domain Event → Sự kiện nghiệp vụ (OrderCreated, PaymentReceived)
├── Repository → Persistence abstraction
└── Domain Service → Logic không thuộc entity nào (PricingCalculator)
Aggregate — Consistency unit
Order Aggregate:
┌───────────────────────────────┐
│ Order (Aggregate Root) │
│ ├── id: "O-001" │
│ ├── status: "confirmed" │
│ ├── total: $500 │
│ │ │
│ ├── OrderItem │
│ │ ├── product: "iPhone" │
│ │ ├── qty: 1 │
│ │ └── price: $400 │
│ │ │
│ ├── OrderItem │
│ │ ├── product: "Case" │
│ │ ├── qty: 1 │
│ │ └── price: $100 │
│ │ │
│ └── ShippingAddress │
│ └── "123 ABC Street" │
└───────────────────────────────┘
Quy tắc:
- Chỉ truy cập thông qua Aggregate Root (Order)
- Một transaction = một aggregate
- Cross-aggregate = eventual consistency
4. Loose Coupling & High Cohesion
4.1 Loose Coupling
Changes in service A do not require changes in service B.
✅ Loose Coupling:
Order Service ──event──▶ Kafka ──▶ Payment Service
(Thay đổi internal logic của Order → Payment không bị ảnh hưởng)
❌ Tight Coupling:
Order Service ──direct DB query──▶ Payment Database
(Thay đổi schema Payment DB → Order Service bị broken)
4.2 High Cohesion
Related functions are grouped together in one service.
✅ High Cohesion:
Payment Service:
├── ProcessPayment()
├── RefundPayment()
├── ValidateCard()
└── GetPaymentHistory()
→ Tất cả đều liên quan đến "thanh toán"
❌ Low Cohesion:
MiscService:
├── ProcessPayment()
├── SendEmail()
├── GenerateReport()
└── ResizeImage()
→ Không liên quan gì đến nhau
5. When should/shouldn't you use Microservices?
5.1 SHOULD be used when
- ✅ large scale system, many teams developing in parallel
- ✅ Need to scale each part of the system independently
- ✅ Requires polyglot (multiple languages/frameworks)
- ✅ Fast release cycle, continuous deployment
- ✅ Complex domain, with clear boundaries
- ✅ The organization has DevOps maturity (CI/CD, monitoring, container orchestration)
5.2 Should NOT be used when
- ❌ Team small (< 5 people)
- ❌ simple application, less complicated domain
- ❌ Not clearly understood domain boundary
- ❌ No CI/CD infrastructure and container orchestration
- ❌ Time for limited development
- ❌ The team does not have experience operating a distributed system
5.3 Monolith First
Martin Fowler recommends "Monolith First":
Phase 1: Monolith
├── Hiểu rõ domain
├── Phát triển nhanh
└── Xác định boundary tự nhiên
Phase 2: Modular Monolith
├── Tách module rõ ràng trong monolith
├── Mỗi module có boundary riêng
└── Giao tiếp qua internal API
Phase 3: Microservices (khi cần)
├── Tách module thành service
├── Thêm API Gateway
└── Event-driven communication
6. Anti-patterns should be avoided
Distributed Monolith
❌ Chia thành nhiều service nhưng:
- Deploy phải đồng thời tất cả
- Shared database
- Synchronous chain calls dài
- Tightly coupled API contracts
→ Có đầy đủ nhược điểm của cả Monolith VÀ Microservices
→ Không có ưu điểm của cái nào cả
Nano-services (too small)
❌ Chia quá nhỏ:
├── CreateOrderService
├── UpdateOrderService
├── DeleteOrderService
├── GetOrderService
└── ListOrderService
→ Quá nhiều service, overhead network, khó quản lý
→ Nên gom thành: OrderService
7. Summary
| Principle | Key Point |
|---|---|
| SRP | Each service has a business, a reason to change |
| DDD | Divide services according to Bounded Context, not according to technique |
| Aggregates | Consistency unit, accessible via Aggregate Root |
| Loose Coupling | Changing service A does not affect service B |
| High Cohesion | Related functions are grouped together into one service |
| Monolith First | Start monolith, gradually separate when understanding domain |
Next article: Synchronous Communication — REST API & gRPC, the two most popular protocols for synchronous communication between microservices.