Giao diện
🎯 Mục tiêu
🎯 Sau bài này bạn sẽ nắm được:
- Tư duy tiếp cận System Design như một Principal Architect — không phải học thuộc, mà là tư duy có hệ thống
- 4 trụ cột thiết kế: Scalability, Reliability, Performance, Cost — và cách cân bằng chúng
- Framework 5 bước để phân tích bất kỳ bài toán thiết kế nào
- Triết lý trade-off: tại sao không có thiết kế hoàn hảo, chỉ có thiết kế phù hợp
- Nhận diện và tránh 4 sai lầm phổ biến nhất khi thiết kế hệ thống
System Design Thinking — Tư duy Kiến trúc sư
Năm 2017, Airbnb có hơn 100 triệu lượt đặt phòng mỗi năm — tất cả chạy trên một monolith Ruby on Rails khổng lồ. Mỗi lần deploy mất hơn 40 phút. Một thay đổi nhỏ ở module thanh toán có thể làm sập tính năng tìm kiếm. Đội ngũ hơn 1,000 engineer push code vào cùng một codebase, merge conflict trở thành công việc hàng ngày. Chi phí vận hành tăng nhanh hơn doanh thu.
Airbnb phải mất gần 3 năm và hàng trăm engineer-years để tách monolith thành kiến trúc service-oriented. Không phải vì họ thiếu kỹ năng — mà vì thiết kế ban đầu không tính đến quy mô 100 triệu users. Bài học đau đớn nhất: chi phí redesign đắt gấp 10–100 lần chi phí design đúng từ đầu.
Đây là lý do bạn cần System Design Thinking — không phải để thiết kế hoàn hảo ngay từ đầu (điều đó bất khả thi), mà để đặt đúng câu hỏi, nhận diện đúng trade-off, và đưa ra quyết định có ý thức thay vì để hệ thống "tự phát triển" theo hướng không kiểm soát.
Bức tranh tư duy
Hãy tưởng tượng bạn là kiến trúc sư quy hoạch một thành phố mới.
Đường giao thông là network — bạn cần quyết định bao nhiêu làn xe (bandwidth), đèn giao thông đặt ở đâu (load balancer), và đường nào là cao tốc (fast path) vs đường nội thành (standard path). Hệ thống nước và điện là data flow — cần đủ áp suất cho mọi toà nhà (throughput), có máy phát dự phòng khi mất điện (redundancy), và đường ống không bị nghẽn giờ cao điểm (back-pressure). Toà nhà là services — mỗi toà có chức năng riêng (single responsibility), có cửa ra vào rõ ràng (API contracts), và có thể xây thêm tầng khi dân số tăng (scalability). Luật quy hoạch là constraints — ngân sách (cost), quy định phòng cháy (reliability requirements), giới hạn chiều cao toà nhà (technical limitations).
Không ai thiết kế cả thành phố trong một ngày. Bạn bắt đầu với master plan, rồi chi tiết hoá từng khu vực. System Design cũng vậy.
Giới hạn của analogy
Thành phố phát triển qua hàng thập kỷ, hệ thống phần mềm có thể scale trong vài giờ nhờ cloud. Nhưng nguyên tắc quy hoạch — dự tính tăng trưởng, phân vùng chức năng, hạ tầng dự phòng — hoàn toàn tương đồng.
Cốt lõi kỹ thuật
4 trụ cột thiết kế
Mọi quyết định System Design đều xoay quanh 4 trụ cột. Chúng luôn mâu thuẫn với nhau — công việc của architect là tìm điểm cân bằng phù hợp với bối cảnh cụ thể.
| Trụ cột | Câu hỏi cốt lõi | Metric đo lường | Ví dụ thực tế |
|---|---|---|---|
| Scalability | Hệ thống xử lý được khi traffic tăng 10x không? | QPS, concurrent users, data volume | Netflix xử lý 400K+ requests/giây trong giờ cao điểm |
| Reliability | Hệ thống có tiếp tục hoạt động khi thành phần X chết? | Uptime (99.9% vs 99.99%), MTTR, MTBF | AWS S3 đạt 99.999999999% durability (11 chữ số 9) |
| Performance | User có cảm nhận hệ thống nhanh không? | Latency (p50, p95, p99), throughput | Google: mỗi 100ms latency tăng → giảm 1% revenue |
| Cost | Chúng ta có đủ tiền vận hành ở quy mô này không? | $/request, $/GB, $/MAU | Startup đốt $50K/tháng cho infra khi chưa có revenue |
Không có "tối ưu cả 4"
Một hệ thống vừa scale vô hạn, vừa không bao giờ chết, vừa trả lời trong 1ms, vừa rẻ — không tồn tại. Nếu ai đó nói thiết kế của họ đạt cả 4, hãy hỏi họ đang đánh đổi gì mà chưa nhận ra.
Framework 5 bước tiếp cận System Design
Đây là quy trình mà các Principal Architect thực sự sử dụng — dù đang thiết kế hệ thống mới hay phỏng vấn System Design.
Bước 1: Clarify Requirements — "Tôi đang xây cái gì?"
Trước khi vẽ bất kỳ diagram nào, hãy hỏi cho đến khi không còn giả định. Phân biệt rõ hai loại yêu cầu:
Functional Requirements — Hệ thống làm gì:
- User có thể đăng ảnh không?
- Có cần hỗ trợ comments, likes?
- Feed hiển thị theo thứ tự nào?
Non-Functional Requirements — Hệ thống làm tốt đến mức nào:
- Latency target: p99 < 200ms hay < 50ms?
- Availability: 99.9% (8.7 giờ downtime/năm) hay 99.99% (52 phút/năm)?
- Data consistency: strong hay eventual?
- Dự kiến quy mô: 10K hay 10M DAU?
Mẹo phỏng vấn
Trong phỏng vấn System Design, dành 3–5 phút đầu chỉ để hỏi requirements. Interviewer đánh giá khả năng đặt câu hỏi cao hơn khả năng vẽ diagram.
Bước 2: Back-of-Envelope Estimation — "Quy mô thực sự là bao nhiêu?"
Tính toán sơ bộ giúp bạn loại bỏ những thiết kế không khả thi trước khi đầu tư thời gian chi tiết. Ba con số cần ước lượng:
QPS (Queries Per Second):
10M DAU × 5 actions/user/ngày = 50M actions/ngày
50M / 86,400 giây ≈ 580 QPS (trung bình)
Peak = 3× trung bình ≈ 1,740 QPSStorage:
50M posts/ngày × 1 KB/post = 50 GB/ngày
× 365 ngày × 3 năm = ~55 TBBandwidth:
1,740 QPS (peak) × 2 KB/response = ~3.5 MB/s outboundNhững con số này quyết định bạn cần 1 server hay 100 server, SQL hay NoSQL, cache hay không cache.
Bước 3: High-Level Design — "Các mảnh ghép là gì?"
Vẽ ra các thành phần chính và cách chúng giao tiếp. Ở bước này, chưa cần chi tiết — chỉ cần đúng các building blocks.
Checklist cho high-level design:
- Mỗi component có chức năng rõ ràng không?
- Data flow từ client đến storage có hợp lý không?
- Có single point of failure nào không?
- Đọc-ghi tách biệt chưa? (read-heavy vs write-heavy khác nhau hoàn toàn)
Bước 4: Deep Dive — "Bottleneck nằm ở đâu?"
Chọn 1–2 component quan trọng nhất để thiết kế chi tiết. Tập trung vào:
- Database schema và indexing strategy
- Caching layer — cache gì, TTL bao lâu, invalidation ra sao
- Sharding strategy — shard theo user_id hay timestamp?
- Failure scenarios — điều gì xảy ra khi component X chết?
Đây là nơi phân biệt junior và senior engineer. Junior vẽ diagram đẹp. Senior tìm ra điểm sẽ gãy đầu tiên khi traffic tăng.
Bước 5: Trade-off Discussion — "Tại sao chọn A, không chọn B?"
Mỗi quyết định thiết kế là một trade-off. Architect giỏi không phải người chọn đúng — mà là người biết mình đang đánh đổi gì và chấp nhận có ý thức.
Ví dụ:
- "Tôi chọn eventual consistency cho feed vì user không cần thấy post ngay lập tức — đổi lại throughput cao hơn 10x."
- "Tôi chọn SQL thay vì NoSQL vì data có quan hệ phức tạp — đánh đổi horizontal scaling sẽ khó hơn ở 10M users."
Tư duy Trade-off — "Không có thiết kế hoàn hảo"
Đây là triết lý quan trọng nhất của System Design: mọi quyết định đều có cái giá. Architect giỏi không tìm giải pháp hoàn hảo — họ tìm giải pháp mà cái giá phải trả là chấp nhận được.
Ma trận trade-off cơ bản
| Trade-off | Chọn A | Chọn B | Khi nào chọn A | Khi nào chọn B |
|---|---|---|---|---|
| Consistency vs Availability | Strong consistency — mọi read đều thấy write mới nhất | High availability — hệ thống luôn trả lời, dù data có thể cũ | Banking, inventory | Social feed, analytics |
| Latency vs Throughput | Low latency — mỗi request nhanh | High throughput — xử lý nhiều request/giây | Real-time gaming, trading | Batch processing, ETL |
| Simplicity vs Flexibility | Monolith — đơn giản, dễ debug | Microservices — linh hoạt, scale độc lập | Startup giai đoạn đầu, team < 10 | Hệ thống lớn, team > 50 |
| Build vs Buy | Tự xây dựng — kiểm soát hoàn toàn | Dùng managed service — nhanh, ít vận hành | Core business logic, competitive advantage | Commodity (auth, email, payment) |
| Cost vs Performance | Tối ưu chi phí — chấp nhận chậm hơn | Tối ưu tốc độ — chấp nhận đắt hơn | Startup bootstrap, internal tools | Customer-facing, revenue-critical |
CAP Theorem — Giới hạn vật lý
Trong hệ thống phân tán, khi xảy ra network partition (P), bạn chỉ có thể chọn một trong hai: Consistency (C) hoặc Availability (A). Đây không phải design choice — đây là giới hạn toán học đã được chứng minh. Hiểu CAP giúp bạn không lãng phí thời gian tìm giải pháp không tồn tại.
Day 1 vs Day 100 — Hệ thống tiến hoá thế nào?
Thiết kế cho 100 users khác hoàn toàn với thiết kế cho 100M users. Sai lầm phổ biến nhất: thiết kế cho 100M users khi bạn chỉ có 100 users (over-engineering), hoặc giữ nguyên thiết kế 100 users khi đã có 1M users (under-engineering).
| Giai đoạn | Users | Kiến trúc | Database | Caching | Deployment |
|---|---|---|---|---|---|
| Day 1 — Startup MVP | 100–1K | Monolith, 1 server | Single PostgreSQL | Không cần | 1 VPS, manual deploy |
| Day 30 — Product-Market Fit | 1K–100K | Monolith + read replicas | PostgreSQL + 1 read replica | Redis cho hot data | 2-3 servers, CI/CD |
| Day 100 — Growth | 100K–1M | Modular monolith / SOA | Sharded database | Multi-tier cache (local + Redis + CDN) | Kubernetes, auto-scaling |
| Day 365 — Scale | 1M–100M | Microservices | Polyglot persistence | Distributed cache cluster | Multi-region, blue-green deploy |
Điều không đổi qua mọi giai đoạn:
- Monitoring và alerting luôn cần từ Day 1
- Database backup và recovery plan
- API contract rõ ràng giữa các component
- Logging có cấu trúc (structured logging)
Điều thay đổi:
- Kiến trúc từ monolith → microservices
- Database từ single node → sharded cluster
- Deploy từ manual → fully automated
- Team từ 3 fullstack → 50+ chuyên biệt
Nguyên tắc vàng
Thiết kế cho 10× quy mô hiện tại, không phải 1000×. Nếu bạn có 10K users, hãy thiết kế cho 100K. Khi đến 100K, redesign cho 1M. Thiết kế cho 100M khi chỉ có 10K users là lãng phí tài nguyên và thời gian.
Sai lầm điển hình
⚠️ Cạm bẫy
Sai lầm 1: Over-engineering ngay từ đầu
Vấn đề: Startup 3 người xây microservices với Kubernetes, Kafka, và service mesh — khi chỉ có 50 users.
Tại sao sai: Mỗi component thêm vào hệ thống tăng complexity theo cấp số nhân, không cấp số cộng. 3 người vận hành 20 microservices = mỗi người chịu trách nhiệm 7 services, chưa kể networking, monitoring, và deployment pipeline. Thay vì ship feature, team dành 80% thời gian xử lý infrastructure.
Cách đúng: Bắt đầu với monolith có cấu trúc tốt (modular monolith). Tách service khi có bằng chứng cụ thể rằng module đó cần scale độc lập — không phải khi bạn "cảm thấy" nên tách.
⚠️ Cạm bẫy
Sai lầm 2: Bỏ qua Non-Functional Requirements
Vấn đề: Thiết kế chỉ tập trung vào "làm gì" (functional) mà quên "làm tốt đến mức nào" (non-functional). Hệ thống hoạt động đúng ở 100 users, nhưng sập ở 10K users.
Tại sao sai: Non-functional requirements (latency, availability, throughput) quyết định kiến trúc, không phải functional requirements. Một CRUD app cho 100 users và một CRUD app cho 10M users có functional requirements giống hệt nhau — nhưng kiến trúc hoàn toàn khác.
Cách đúng: Luôn xác định rõ: target QPS, latency SLA (p50 và p99), availability target (99.9% vs 99.99%), và data retention policy trước khi bắt đầu thiết kế.
⚠️ Cạm bẫy
Sai lầm 3: Resume-Driven Design
Vấn đề: Chọn công nghệ vì muốn thêm vào CV, không phải vì bài toán cần. "Chúng ta dùng Kafka nhé" — khi hệ thống có 100 messages/ngày.
Tại sao sai: Mỗi công nghệ mang theo operational cost: learning curve, monitoring, backup, upgrade, troubleshooting. Kafka cho 100 messages/ngày giống như thuê Boeing 747 để chở 3 người đi Đà Nẵng.
Cách đúng: Bắt đầu từ bài toán, không phải công nghệ. Hỏi: "Tôi cần gì?" trước "Tôi nên dùng gì?". Nếu Redis Queue giải quyết được, đừng dùng Kafka. Nếu cron job đủ, đừng dùng message queue.
⚠️ Cạm bẫy
Sai lầm 4: Không làm Back-of-Envelope Math
Vấn đề: Thiết kế mà không ước lượng QPS, storage, bandwidth. Kết quả: chọn database không chịu nổi tải, hoặc ngược lại — mua server gấp 100 lần cần thiết.
Tại sao sai: Không có con số = không có cơ sở để ra quyết định. "Hệ thống cần xử lý nhiều request" — nhiều là bao nhiêu? 100/s, 10K/s, hay 1M/s? Mỗi mức đòi hỏi kiến trúc hoàn toàn khác nhau.
Cách đúng: Luôn ước lượng 3 con số: QPS (bao nhiêu requests/giây), Storage (bao nhiêu dữ liệu tích luỹ trong 3–5 năm), Bandwidth (bao nhiêu MB/s ra/vào). Sai 2–5 lần vẫn hữu ích hơn không có con số nào.
✅ Checklist triển khai
Checklist tư duy trước mỗi thiết kế
Requirements
- [ ] Liệt kê đầy đủ functional requirements — hệ thống làm gì?
- [ ] Xác định non-functional requirements — latency, availability, consistency targets cụ thể
- [ ] Phân biệt rõ "must-have" vs "nice-to-have" — tránh scope creep
- [ ] Xác định user personas và use cases chính — ai dùng, dùng thế nào?
Estimation
- [ ] Ước lượng QPS — average và peak (thường peak = 3–5× average)
- [ ] Tính storage cho 3–5 năm — bao gồm cả replication và backup
- [ ] Tính bandwidth — inbound + outbound × peak multiplier
- [ ] Xác định read:write ratio — quyết định caching strategy
Architecture
- [ ] Vẽ high-level diagram trước khi viết bất kỳ dòng code nào
- [ ] Xác định single points of failure — mỗi component đều phải có fallback
- [ ] Chọn communication pattern — sync (REST/gRPC) vs async (message queue)
- [ ] Data flow rõ ràng — từ client → API → storage, không có vòng lặp ẩn
Trade-offs
- [ ] Ghi lại mọi quyết định trade-off — "Chọn A vì..., chấp nhận đánh đổi B"
- [ ] Xác định giới hạn của thiết kế — "Thiết kế này hoạt động tốt đến X users, sau đó cần redesign"
- [ ] Xem xét Build vs Buy cho mỗi component — đừng tự build commodity
- [ ] Validate với team — một architect không bao giờ thiết kế một mình
Operational Readiness
- [ ] Monitoring và alerting plan — biết hệ thống chết trước khi user biết
- [ ] Disaster recovery — backup, failover, data recovery procedure
- [ ] Deployment strategy — blue-green, canary, hay rolling update?
- [ ] Capacity planning — khi nào cần scale, trigger là gì?
Bài tập luyện tập
🧠 Quiz
Câu 1: Một startup fintech đang xây hệ thống chuyển tiền. Họ cần chọn giữa strong consistency và high availability. Theo CAP theorem, đâu là lựa chọn đúng?
- [x] Strong consistency — vì giao dịch tài chính không thể chấp nhận dữ liệu sai
- [ ] High availability — vì hệ thống luôn phải online
- [ ] Cả hai đều đạt được nếu thiết kế tốt
- [ ] Tuỳ vào ngân sách
Giải thích: Trong fintech, một giao dịch bị ghi đúp hoặc mất tiền là thảm hoạ. Consistency phải được ưu tiên — chấp nhận hệ thống tạm thời không available (trả lỗi) còn hơn trả kết quả sai. Đây là lý do hầu hết hệ thống banking dùng ACID transactions.
🧠 Quiz
Câu 2: Team của bạn có 5 engineer, sản phẩm có 2K DAU. CTO đề xuất chuyển sang microservices. Bạn nên phản hồi thế nào?
- [ ] Đồng ý — microservices là best practice hiện đại
- [ ] Đồng ý nhưng bắt đầu với 3 services trước
- [x] Phản đối — đề xuất modular monolith, chỉ tách service khi có bằng chứng cụ thể cần scale độc lập
- [ ] Đề xuất serverless thay thế
Giải thích: 5 engineer + 2K DAU = monolith là lựa chọn tối ưu. Microservices tăng operational complexity (networking, deployment, monitoring, debugging distributed systems) mà 5 người không thể vận hành hiệu quả. Modular monolith cho phép tách service sau này khi thực sự cần, mà không phải trả chi phí phân tán trước.
🧠 Quiz
Câu 3: Hệ thống social media có 50M DAU, mỗi user xem feed 10 lần/ngày. Ước lượng QPS cho feed API là bao nhiêu?
- [ ] 50,000 QPS
- [ ] 500,000 QPS
- [x] ~5,800 QPS trung bình, ~17,400 QPS peak
- [ ] 5,000,000 QPS
Giải thích: 50M DAU × 10 views/ngày = 500M requests/ngày. 500M / 86,400 giây ≈ 5,787 QPS (trung bình). Peak thường gấp 3× trung bình ≈ 17,361 QPS. Đây là mức cần horizontal scaling (nhiều server + load balancer), nhưng chưa đến mức cần kiến trúc cực kỳ phức tạp.
🧠 Quiz
Câu 4: Đâu là thứ tự đúng của 5 bước tiếp cận System Design?
- [ ] High-level design → Estimation → Requirements → Deep dive → Trade-offs
- [ ] Requirements → High-level design → Deep dive → Estimation → Trade-offs
- [x] Requirements → Estimation → High-level design → Deep dive → Trade-offs
- [ ] Estimation → Requirements → High-level design → Trade-offs → Deep dive
Giải thích: Phải hiểu yêu cầu trước (Requirements), rồi ước lượng quy mô (Estimation) để biết cần kiến trúc phức tạp đến đâu, sau đó mới vẽ tổng quan (High-level design), đi sâu vào chi tiết (Deep dive), và cuối cùng thảo luận đánh đổi (Trade-offs). Estimation trước high-level design vì con số quy mô quyết định bạn cần bao nhiêu component.