C4 Model Thực Chiến - Từ Dự Án Thực Tế Đến Tài Liệu Kiến Trúc Chuyên Nghiệp
Hành trình áp dụng C4 Model để tài liệu hóa kiến trúc hệ thống EdTech thực tế - từ Context đến Component, những bài học rút ra và tại sao nó thay đổi cách team mình làm việc.
Nghe bài viết này dưới dạng podcastC4 Model Thực Chiến - Từ Dự Án Thực Tế Đến Tài Liệu Kiến Trúc Chuyên Nghiệp
Có một câu hỏi mà bất kỳ developer nào cũng đã từng gặp: "Hệ thống này hoạt động như thế nào?"
Câu trả lời thường là: mở Slack ping người cũ, đọc code, hoặc tệ hơn là... đoán.
Sau nhiều năm làm việc trong các dự án lớn nhỏ, mình nhận ra rằng tài liệu kiến trúc không phải thứ xa xỉ - nó là điều kiện tối thiểu để một team làm việc hiệu quả. Và C4 Model là công cụ đã thay đổi cách mình tiếp cận vấn đề này.
C4 Model Là Gì? (Và Tại Sao Không Phải UML)
C4 Model do Simon Brown tạo ra, với triết lý đơn giản: tài liệu kiến trúc nên giống bản đồ - có nhiều cấp độ zoom khác nhau tùy theo người xem cần gì.
Bốn cấp độ:
| Cấp | Tên | Đối tượng | Nội dung |
|---|---|---|---|
| 1 | Context | Stakeholder, PM, CEO | Hệ thống làm gì, tương tác với ai |
| 2 | Container | Developer, Architect | Các thành phần chạy độc lập (app, DB, queue) |
| 3 | Component | Developer | Bên trong một container có gì |
| 4 | Code | Developer | Class diagram, hiếm khi cần |
Lý do mình chọn C4 thay vì UML thuần túy:
- UML quá chi tiết cho giao tiếp đa tầng - vẽ sequence diagram với CEO là thảm họa.
- C4 có ngôn ngữ chung - dù bạn dùng Draw.io, Mermaid hay PlantUML, cấu trúc vẫn nhất quán.
- Dễ maintain - level 1 & 2 ít thay đổi, level 3 & 4 update khi refactor.
Bối Cảnh Dự Án Thực Tế
Mình sẽ lấy ví dụ từ edu-manager - một nền tảng EdTech cho học sinh tiểu học và THPT tại Việt Nam. Stack chính:
- Backend: NestJS + PostgreSQL + pgvector + Redis
- Frontend: Next.js
- AI Layer: Mastra AI (embedded trong NestJS)
- Infrastructure: AWS (EC2, S3) + Coolify
Hệ thống có các tính năng phức tạp: quiz agent với HITL (Human-in-the-Loop), RAG pipeline cho tài liệu giáo dục, và CopilotKit để drive UI từ AI agent.
Khi team mở rộng và onboard developer mới, câu hỏi "hệ thống này hoạt động thế nào?" xuất hiện liên tục. Đó là lúc mình quyết định xây dựng bộ tài liệu C4 bài bản.
Level 1: System Context Diagram
Level 1 trả lời câu hỏi: "Hệ thống của chúng ta ở đâu trong bức tranh lớn hơn?"
graph TB
Student["👦 Học Sinh<br/>(Người dùng chính)"]
Teacher["👩🏫 Giáo Viên<br/>(Quản lý nội dung)"]
Parent["👨👩👧 Phụ Huynh<br/>(Theo dõi tiến độ)"]
EduManager["🎓 edu-manager<br/>(Nền tảng học tập AI)"]
OpenAI["☁️ OpenAI API<br/>(Vision, Embeddings)"]
Groq["☁️ Groq API<br/>(LLM Inference)"]
AWSS3["☁️ AWS S3<br/>(File Storage)"]
Email["📧 Email Service<br/>(Thông báo)"]
Student -->|"Học bài, làm quiz"| EduManager
Teacher -->|"Upload tài liệu, tạo bài tập"| EduManager
Parent -->|"Xem báo cáo tiến độ"| EduManager
EduManager -->|"OCR, embeddings"| OpenAI
EduManager -->|"Chat, quiz generation"| Groq
EduManager -->|"Lưu file PDF, ảnh"| AWSS3
EduManager -->|"Gửi thông báo"| Email
Bài học rút ra từ level này:
Khi vẽ lần đầu, mình có xu hướng đưa quá nhiều chi tiết vào - vẽ cả Redis, cả Kafka. Đó là sai lầm. Level 1 chỉ nên có hệ thống của bạn + external actors. Nếu stakeholder phải hỏi "cái hình vuông đó là gì?" thì bạn đã overload diagram rồi.
Level 2: Container Diagram
Level 2 trả lời: "Bên trong hệ thống có những gì chạy độc lập?"
Đây là level quan trọng nhất với developer. Mọi người thường bỏ qua level 1 và muốn nhảy thẳng vào đây - điều đó ổn.
graph TB
subgraph "edu-manager System"
NextApp["🌐 Next.js App<br/>(React, TypeScript)<br/>Port 3000"]
NestAPI["⚙️ NestJS API<br/>(REST + WebSocket)<br/>Port 4000"]
MastraAI["🤖 Mastra AI Engine<br/>(Embedded trong NestJS)<br/>Agent Orchestration"]
PostgreSQL["🗄️ PostgreSQL + pgvector<br/>(Primary Database)"]
Redis["⚡ Redis<br/>(Cache + Session + Queue)"]
AWSS3["📦 AWS S3<br/>(File Storage)"]
end
Browser["🌍 Browser / Mobile"]
OpenAI["☁️ OpenAI API"]
Groq["☁️ Groq API"]
Browser -->|"HTTPS"| NextApp
NextApp -->|"REST API / WebSocket"| NestAPI
NestAPI -->|"TypeORM / Prisma"| PostgreSQL
NestAPI -->|"ioredis"| Redis
NestAPI -->|"AWS SDK"| AWSS3
MastraAI -->|"OpenAI SDK"| OpenAI
MastraAI -->|"Groq SDK"| Groq
NestAPI -->|"internal call"| MastraAI
Quyết định kiến trúc quan trọng được thể hiện ở đây:
Mastra AI được embed bên trong NestJS thay vì tách thành service riêng. Đây là một ADR (Architecture Decision Record) cần ghi lại riêng. Lý do: ở giai đoạn MVP, việc maintain thêm một service độc lập tăng overhead không cần thiết. Trade-off: khó scale AI layer độc lập khi cần.
Level 2 là nơi những trade-off như vậy trở nên rõ ràng và buộc team phải đưa ra quyết định có ý thức.
Level 3: Component Diagram
Level 3 đi sâu vào bên trong một container cụ thể. Không cần vẽ tất cả - chỉ vẽ những phần phức tạp hoặc hay bị hỏi.
Với edu-manager, mình chọn vẽ NestJS API vì đây là nơi business logic tập trung nhất:
graph TB
subgraph "NestJS API Container"
AuthModule["🔐 Auth Module<br/>(JWT, Guards)"]
QuizModule["📝 Quiz Module<br/>(HITL Flow)"]
RAGModule["🔍 RAG Module<br/>(pgvector Search)"]
CharacterModule["🐾 Character Module<br/>(Capybara RPG)"]
OCRModule["👁️ OCR Module<br/>(PDF Processing)"]
MastraModule["🤖 Mastra Module<br/>(Agent Orchestration)"]
end
NextFE["Next.js Frontend"]
DB["PostgreSQL"]
Redis["Redis"]
NextFE -->|"REST"| AuthModule
NextFE -->|"WebSocket"| QuizModule
QuizModule -->|"trigger agent"| MastraModule
MastraModule -->|"vector search"| RAGModule
RAGModule -->|"pgvector query"| DB
OCRModule -->|"store embeddings"| RAGModule
QuizModule -->|"cache state"| Redis
CharacterModule -->|"dialogue state"| DB
Lưu ý thực tế: Ở level 3, bạn sẽ bắt đầu thấy circular dependencies và coupling ngầm mà trước đây không ai nhận ra. Khi mình vẽ component diagram lần đầu, phát hiện ra QuizModule đang gọi thẳng vào OCRModule - một coupling không hợp lý về mặt domain. Diagram đã giúp team thấy vấn đề trước khi nó trở thành tech debt lớn.
Workflow Thực Tế: Từ Whiteboard Đến GitHub Wiki
Quy trình mình áp dụng:
1. Draft trên whiteboard (hoặc Excalidraw)
Đừng nghĩ đến tool ngay. Vẽ tay nhanh, thảo luận với team, sửa cho đến khi mọi người đồng ý về big picture.
2. Chuyển sang Mermaid trong Markdown
## Container Diagram
\```mermaid
graph TB
...
\```
### Ghi chú
- [ADR-001] Mastra AI embedded vs standalone service
- [ADR-002] PostgreSQL + pgvector thay vì Pinecone
\```
3. Lưu trên GitHub Wiki
GitHub Wiki render Mermaid natively từ 2022. Không cần tool phức tạp, không cần export PNG, diagram luôn up-to-date với code.
4. Link từ README
## Architecture
📐 [System Architecture](../../wiki/System-Architecture-Document)
Những Sai Lầm Phổ Biến (Và Cách Tránh)
❌ Vẽ tất cả 4 level ngay từ đầu Bắt đầu với Level 1 và 2. Level 3 chỉ vẽ khi có developer mới join hoặc khi refactor module phức tạp.
❌ Dùng quá nhiều màu sắc và icon C4 Model có color convention đơn giản: xanh dương cho internal, xám cho external. Stick to it.
❌ Không ghi chú quyết định kiến trúc Diagram chỉ trả lời "cái gì", không trả lời "tại sao". Luôn đi kèm với ADR cho những quyết định quan trọng.
❌ Treat diagram như sản phẩm cuối cùng Diagram là công cụ giao tiếp, không phải tác phẩm nghệ thuật. Nếu mất 3 tiếng để làm diagram đẹp, bạn đang làm sai rồi.
Kết Quả Sau Khi Áp Dụng
Sau khi hoàn thiện bộ tài liệu C4 cho edu-manager:
- Onboarding developer mới từ 2-3 ngày "hỏi han" xuống còn buổi sáng đầu tiên tự đọc.
- Review kiến trúc với stakeholder dễ dàng hơn - show Level 1 là đủ cho business discussion.
- Phát hiện design issue sớm hơn thông qua việc "buộc phải vẽ ra" thay vì chỉ nghĩ trong đầu.
- ADR trở thành habit - mỗi quyết định kiến trúc quan trọng đều có document kèm theo.
Tool Gợi Ý
| Mục đích | Tool | Lý do |
|---|---|---|
| Draft nhanh | Excalidraw | Free, collaborative, không cần login |
| Diagram chính thức | Mermaid trong Markdown | Version control, GitHub native |
| Diagram phức tạp | Draw.io (diagrams.net) | Export XML, diff được |
| C4 dedicated | Structurizr | Đúng chuẩn nhất, nhưng có learning curve |
Với team nhỏ đến trung bình, Mermaid + GitHub Wiki là sweet spot. Không over-engineer tool khi bạn đang cần focus vào product.
Tổng Kết
C4 Model không phải magic bullet. Nó không tự viết code cho bạn, không fix bug, không replace pair programming.
Nhưng nó cho bạn thứ quý giá hơn: ngôn ngữ chung để team nói chuyện về kiến trúc. Khi mọi người cùng nhìn vào một diagram và hiểu như nhau, những cuộc họp 2 tiếng rút ngắn xuống 20 phút. Những PR review "tại sao lại làm thế này?" có câu trả lời rõ ràng từ ADR.
Nếu bạn chưa có tài liệu kiến trúc, hãy bắt đầu ngay hôm nay với Level 1. Chỉ cần một tờ giấy và bút - không cần tool fancy. Phần còn lại sẽ tự nhiên theo.
Bạn đang dùng C4 Model hay approach khác để document kiến trúc? Comment bên dưới nhé - mình rất muốn nghe về kinh nghiệm thực tế của các bạn.
Chưa có bình luận
Để lại bình luận
Bình luận sẽ được phê duyệt trước khi hiển thị.