Skip to content

🏛️ MONOLITH VS MICROSERVICES

Trade-offs, Modular Monolith, và Database per Service


📋 Prerequisites

📚 Kiến thức cần có

Trước khi bắt đầu module này, hãy đảm bảo bạn đã nắm vững:


🎯 Mục tiêu

Sau khi hoàn thành module này, bạn sẽ:

  1. Hiểu rõ trade-offs giữa Monolith và Microservices
  2. Biết khi nào nên chọn Modular Monolith
  3. Nắm vững nguyên tắc "Database per Service"
  4. Nhận diện và tránh Distributed Monolith anti-pattern

🏰 The Majestic Monolith

Monolith không phải là "xấu"

Có một sự thật mà nhiều developer bỏ qua: những hệ thống lớn nhất thế giới vẫn chạy trên Monolith.

📊 Case Study: Stack Overflow

Stack Overflow - website Q&A lớn nhất thế giới với 100+ triệu monthly visitors - chạy trên một Monolith duy nhất.

MetricGiá trị
Monthly Visitors100+ triệu
Questions23+ triệu
Answers34+ triệu
ArchitectureMonolith (ASP.NET)
Servers~9 web servers
Database2 SQL Server clusters

💡 Tại sao Stack Overflow chọn Monolith?

  • Simplicity: Một codebase, một deployment, một team hiểu toàn bộ hệ thống
  • Performance: Không có network latency giữa các services
  • Debugging: Stack trace đầy đủ, dễ trace bugs
  • Cost: Ít infrastructure, ít DevOps overhead

🛒 Case Study: Shopify

Shopify - nền tảng e-commerce với $175 billion GMV - cũng chạy trên Modular Monolith.

Shopify Architecture (2023):
├── Core Monolith (Ruby on Rails)
│   ├── Checkout Module
│   ├── Inventory Module
│   ├── Orders Module
│   └── Payments Module
└── Extracted Services (chỉ khi cần thiết)
    ├── Storefront Renderer
    └── Background Jobs

"We've been running a monolith for 15 years. It's not about monolith vs microservices, it's about modular vs coupled." — Shopify Engineering


🧱 Modular Monolith Architecture

Modular Monolith là kiến trúc kết hợp ưu điểm của cả hai thế giới:

  • Simplicity của Monolith (single deployment)
  • Modularity của Microservices (clear boundaries)

Nguyên tắc thiết kế

┌─────────────────────────────────────────────────────────┐
│                    MODULAR MONOLITH                      │
├─────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │   Module A  │  │   Module B  │  │   Module C  │     │
│  │  ┌───────┐  │  │  ┌───────┐  │  │  ┌───────┐  │     │
│  │  │ API   │  │  │  │ API   │  │  │  │ API   │  │     │
│  │  ├───────┤  │  │  ├───────┤  │  │  ├───────┤  │     │
│  │  │Domain │  │  │  │Domain │  │  │  │Domain │  │     │
│  │  ├───────┤  │  │  ├───────┤  │  │  ├───────┤  │     │
│  │  │ Data  │  │  │  │ Data  │  │  │  │ Data  │  │     │
│  │  └───────┘  │  │  └───────┘  │  │  └───────┘  │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
│                                                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │              SHARED DATABASE                     │   │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐         │   │
│  │  │Schema A │  │Schema B │  │Schema C │         │   │
│  │  └─────────┘  └─────────┘  └─────────┘         │   │
│  └─────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘

Quy tắc vàng của Modular Monolith

Quy tắcMô tả
1. Clear BoundariesMỗi module có API riêng, không truy cập trực tiếp internal của module khác
2. Schema IsolationMỗi module sở hữu schema riêng trong database
3. Communication via APIModules giao tiếp qua public interfaces, không qua database joins
4. Independent TestingMỗi module có thể test độc lập

Code Example: Module Boundaries

typescript
// ❌ BAD: Modules truy cập trực tiếp database của nhau
class OrderService {
  async createOrder(userId: string, items: Item[]) {
    // Truy cập trực tiếp bảng users - VI PHẠM boundary!
    const user = await db.query('SELECT * FROM users WHERE id = ?', [userId]);
    // ...
  }
}

// ✅ GOOD: Modules giao tiếp qua API
class OrderService {
  constructor(private userModule: UserModuleAPI) {}
  
  async createOrder(userId: string, items: Item[]) {
    // Gọi qua public API của User Module
    const user = await this.userModule.getUserById(userId);
    // ...
  }
}

🔬 Microservices: Định nghĩa Đúng

⚠️ Định nghĩa quan trọng

Microservices = Deploy độc lập + Database riêng

Nếu thiếu một trong hai yếu tố này, bạn KHÔNG có Microservices.

Hai điều kiện bắt buộc

1. Deploy độc lập (Independent Deployment)

Mỗi service có thể:

  • Build riêng
  • Test riêng
  • Deploy riêng
  • Scale riêng
yaml
# Kubernetes deployment - mỗi service độc lập
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 3  # Scale riêng
  template:
    spec:
      containers:
      - name: order-service
        image: order-service:v2.1.0  # Version riêng
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
spec:
  replicas: 5  # Scale khác với order-service
  template:
    spec:
      containers:
      - name: payment-service
        image: payment-service:v1.8.0  # Version khác

2. Database per Service (Nguyên tắc cốt lõi)

🗃️ DATABASE PER SERVICE

Đây là nguyên tắc QUAN TRỌNG NHẤT của Microservices.

Mỗi service SỞ HỮU data của mình:

  • Không service nào được truy cập trực tiếp database của service khác
  • Giao tiếp data chỉ qua API hoặc Events
┌─────────────────────────────────────────────────────────┐
│                    MICROSERVICES                         │
├─────────────────────────────────────────────────────────┤
│                                                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │Order Service│  │Payment Svc  │  │User Service │     │
│  │  ┌───────┐  │  │  ┌───────┐  │  │  ┌───────┐  │     │
│  │  │ API   │  │  │  │ API   │  │  │  │ API   │  │     │
│  │  ├───────┤  │  │  ├───────┤  │  │  ├───────┤  │     │
│  │  │Domain │  │  │  │Domain │  │  │  │Domain │  │     │
│  │  └───────┘  │  │  └───────┘  │  │  └───────┘  │     │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘     │
│         │                │                │             │
│         ▼                ▼                ▼             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │  PostgreSQL │  │    MySQL    │  │   MongoDB   │     │
│  │  (Orders)   │  │  (Payments) │  │   (Users)   │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
│                                                          │
└─────────────────────────────────────────────────────────┘

Tại sao Database per Service quan trọng?

Lợi íchGiải thích
Loose CouplingServices không phụ thuộc vào schema của nhau
Independent ScalingCó thể chọn database phù hợp cho từng service
Fault IsolationDatabase của service A chết không ảnh hưởng service B
Team AutonomyTeam sở hữu toàn bộ stack của service

☠️ Distributed Monolith: The Worst of Both Worlds

☠️ ANTI-PATTERN ALERT: DISTRIBUTED MONOLITH

Distributed Monolith là khi bạn có:

  • Nhiều services (complexity của Microservices)
  • Nhưng vẫn tightly coupled (không có lợi ích của Microservices)

Kết quả: Bạn phải chịu tất cả nhược điểm của cả hai kiến trúc mà không có ưu điểm nào.

Dấu hiệu nhận biết Distributed Monolith

Dấu hiệuMô tả
🔗 Shared DatabaseNhiều services truy cập cùng một database
🔄 Synchronized DeploymentsPhải deploy nhiều services cùng lúc
💥 Cascading FailuresService A chết → Service B, C, D cũng chết
🧪 Integration Testing HellKhông thể test một service độc lập
📞 Synchronous ChainsA → B → C → D (chain of sync calls)

Ví dụ: Shared Database Anti-pattern

❌ DISTRIBUTED MONOLITH (Shared Database)
┌─────────────────────────────────────────────────────────┐
│                                                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │Order Service│  │Payment Svc  │  │User Service │     │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘     │
│         │                │                │             │
│         └────────────────┼────────────────┘             │
│                          │                              │
│                          ▼                              │
│              ┌─────────────────────┐                    │
│              │   SHARED DATABASE   │  ← Coupling point! │
│              │  ┌─────┬─────┬────┐ │                    │
│              │  │users│orders│pay │ │                    │
│              │  └─────┴─────┴────┘ │                    │
│              └─────────────────────┘                    │
│                                                          │
└─────────────────────────────────────────────────────────┘

Vấn đề:
- Order Service đọc trực tiếp bảng users
- Payment Service JOIN với bảng orders
- Thay đổi schema → phải update TẤT CẢ services

Hậu quả của Distributed Monolith

typescript
// Ví dụ: Synchronous Call Chain (Anti-pattern)
class OrderService {
  async createOrder(userId: string, items: Item[]) {
    // 1. Gọi User Service (sync)
    const user = await this.userService.getUser(userId);  // 100ms
    
    // 2. Gọi Inventory Service (sync)
    const stock = await this.inventoryService.checkStock(items);  // 150ms
    
    // 3. Gọi Payment Service (sync)
    const payment = await this.paymentService.charge(user, total);  // 200ms
    
    // 4. Gọi Notification Service (sync)
    await this.notificationService.sendEmail(user.email);  // 100ms
    
    // Total latency: 550ms (cộng dồn!)
    // Nếu BẤT KỲ service nào chết → toàn bộ request fail
  }
}

🔧 Raizo's Reality Check

"Tôi đã thấy nhiều team 'chuyển sang Microservices' nhưng thực ra chỉ tạo ra Distributed Monolith. Họ có 20 services nhưng vẫn phải deploy cùng lúc, vẫn share database, và mỗi lần có bug thì phải debug qua 5-6 services.

Kết quả? Latency tăng 10x, debugging khó hơn 100x, và team vẫn không thể deploy độc lập. Đó là worst of both worlds."


📊 So sánh: Modular Monolith vs Microservices

Architecture Comparison Diagram

Decision Matrix

Tiêu chíModular MonolithMicroservicesDistributed Monolith
DeploymentSingle unitIndependentPhải sync
DatabaseShared (isolated schemas)Per serviceShared (coupled)
Complexity⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Team Size5-5050+N/A (anti-pattern)
DebuggingEasyHardVery Hard
LatencyLowMediumHigh
ScalabilityVertical + some HorizontalFull HorizontalLimited
Recommendation✅ Most teams✅ Large orgs❌ Avoid

🎓 Giáo sư Tom's Insight

🎓 Góc nhìn Lý thuyết

Conway's Law và Architecture

"Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations." — Melvin Conway, 1967

Ý nghĩa thực tế:

  • Nếu bạn có 1 team → Monolith là tự nhiên
  • Nếu bạn có nhiều teams độc lập → Microservices có thể hợp lý

Evolutionary Architecture

Kiến trúc tốt nhất là kiến trúc tiến hóa theo tổ chức:

Startup (5 người)     →  Monolith
Scale-up (20 người)   →  Modular Monolith  
Enterprise (100+ người) →  Microservices (có thể)

The Fallacy of "Future-Proofing"

Nhiều team chọn Microservices vì "sau này sẽ cần scale". Đây là premature optimization.

Sự thật:

  • 90% startups không bao giờ đạt scale cần Microservices
  • Chuyển từ Modular Monolith → Microservices dễ hơn nhiều so với việc fix Distributed Monolith
  • YAGNI (You Aren't Gonna Need It) áp dụng cho architecture

🔧 Kỹ sư Raizo's Reality Check

🔧 Góc nhìn Thực chiến

"Nếu team dưới 50 người, hãy quên Microservices đi"

"Tôi đã làm việc ở cả startup và Big Tech. Đây là sự thật mà ít ai nói:

Microservices được tạo ra để giải quyết vấn đề của TỔ CHỨC, không phải vấn đề KỸ THUẬT.

Amazon, Netflix, Google chuyển sang Microservices vì họ có hàng nghìn engineers cần làm việc độc lập. Không phải vì Microservices 'tốt hơn'."

Chi phí ẩn của Microservices

Chi phíMô tả
DevOps OverheadCI/CD cho mỗi service, Kubernetes, Service Mesh
ObservabilityDistributed tracing, Log aggregation, Metrics
Network ComplexityService discovery, Load balancing, Circuit breakers
Data ConsistencySaga pattern, Eventual consistency, Idempotency
TestingContract testing, Integration testing across services

Câu hỏi tự kiểm tra

Trước khi chọn Microservices, hãy trả lời:

  1. ❓ Team có 50+ engineers không?
  2. ❓ Có nhiều teams cần deploy độc lập không?
  3. ❓ Có DevOps maturity để vận hành không?
  4. ❓ Có sẵn sàng chấp nhận eventual consistency không?

Nếu trả lời "Không" cho bất kỳ câu nào → Chọn Modular Monolith.


💡 Think About It

🤔 Câu hỏi suy ngẫm

  1. Stack Overflow phục vụ 100 triệu users với Monolith. Tại sao nhiều startup 1000 users lại nghĩ họ cần Microservices?

  2. Nếu Shopify - công ty $175 billion GMV - vẫn dùng Modular Monolith, điều đó nói gì về "best practices"?

  3. Conway's Law nói kiến trúc phản ánh tổ chức. Nếu team bạn có 5 người, kiến trúc nào phản ánh đúng nhất?


🎯 Interactive Scenario

🎯 Tình huống: Startup Thương mại Điện tử

Bối cảnh

Bạn là Tech Lead của một startup e-commerce mới thành lập:

Thông tinChi tiết
Team size5 developers (full-stack)
TimelineMVP trong 3 tháng
Expected users10,000 users năm đầu
BudgetLimited (seed funding)
FeaturesProduct catalog, Cart, Checkout, User accounts

CEO đọc được bài viết về Netflix và muốn xây dựng hệ thống "scalable từ đầu" với Microservices.

Câu hỏi

Bạn sẽ đề xuất kiến trúc nào?

A) Microservices - Tách thành 5 services: User, Product, Cart, Order, Payment

B) Modular Monolith - Một ứng dụng với 5 modules có boundaries rõ ràng

C) Simple Monolith - Một ứng dụng đơn giản, refactor sau


🔧 Raizo's Verdict (nếu chọn A - Microservices)

SAI RỒI! 🚫

"5 người mà đòi làm Microservices? Bạn sẽ dành 80% thời gian cho infrastructure thay vì features.

Với 5 services, bạn cần:

  • 5 CI/CD pipelines
  • 5 databases
  • Service discovery
  • API Gateway
  • Distributed tracing
  • Log aggregation

Team 5 người không có bandwidth cho những thứ này. Bạn sẽ ship MVP trong 12 tháng thay vì 3 tháng, và có thể hết tiền trước khi launch."

✅ Đáp án đúng: B hoặc C

Lựa chọn tối ưu: Modular Monolith (B)

Lý do:

  1. Team size phù hợp: 5 người có thể hiểu toàn bộ codebase
  2. Time to market: Ship nhanh hơn 3-5x so với Microservices
  3. Cost effective: Một server, một database, một deployment
  4. Future-proof: Dễ dàng extract thành Microservices SAU KHI có product-market fit

Cấu trúc đề xuất

ecommerce-app/
├── src/
│   ├── modules/
│   │   ├── user/          # User domain
│   │   ├── product/       # Product catalog
│   │   ├── cart/          # Shopping cart
│   │   ├── order/         # Order management
│   │   └── payment/       # Payment processing
│   ├── shared/            # Shared utilities
│   └── main.ts
├── database/
│   └── migrations/        # Single DB, separate schemas
└── tests/

Khi nào chuyển sang Microservices?

  • Team grow lên 20+ engineers
  • product-market fit rõ ràng
  • DevOps resources dedicated
  • bottleneck cụ thể cần scale riêng

:::


📝 Summary

ConceptKey Takeaway
MonolithKhông xấu - Stack Overflow, Shopify vẫn dùng thành công
Modular MonolithBest choice cho hầu hết teams (< 50 engineers)
MicroservicesChỉ khi có organizational need, không phải technical need
Database per ServiceĐiều kiện BẮT BUỘC của Microservices
Distributed MonolithAnti-pattern - worst of both worlds

🚀 Next Steps

Tiếp theo trong Phase 3

👉 Module 3.2: Distributed Transactions →

Học cách xử lý transactions khi data nằm ở nhiều services khác nhau.

TopicLiên quan
📊 Consistency ModelsACID vs BASE - nền tảng cho distributed systems
📬 Async MessagingEvent-driven architecture cho loose coupling
🗃️ Database DesignSharding strategies khi scale

Case Studies áp dụng

Case StudyÁp dụng
🐦 Design TwitterXem cách Twitter tiến hóa từ Monolith
🚗 Design UberMicroservices trong ride-sharing

📖 Further Reading