Chương 0. Tấm Bản Đồ Chung

Kiến trúc hệ thống rất dễ làm người học bị ngợp. Chỉ cần nhìn vào một hệ thống hiện đại, ta đã thấy hàng loạt khái niệm: DNS, CDN, reverse proxy, load balancer, API Gateway, database, cache, queue, worker, Redis, Kafka, Docker, Kubernetes, observability, tracing, SLO, circuit breaker, rate limit, service mesh.

Nếu học từng công nghệ rời rạc, ta rất dễ rơi vào cảm giác "biết nhiều tên nhưng không biết dùng khi nào". Mục tiêu của chương này là dựng một tấm bản đồ chung: mỗi thành phần nằm ở đâu, giải quyết vấn đề gì, và khi hệ thống gặp một loại vấn đề thì nên nhìn vào vùng nào của bản đồ.

Cuốn sách này không xem kiến trúc là cuộc đua dùng công nghệ mới. Kiến trúc tốt là cách kiểm soát độ phức tạp để hệ thống chạy được, đo được, debug được, mở rộng được, và vẫn đủ đơn giản để con người vận hành.

---

0.1. Vì sao cần một bản đồ trước khi học chi tiết?

Một người mới học kiến trúc thường hỏi theo kiểu:

  • Có nên dùng microservices không?
  • Redis khác Kafka thế nào?
  • Dùng FastAPI hay Django?
  • Khi nào cần Kubernetes?
  • CDN, proxy, load balancer, API Gateway khác nhau ra sao?
  • Message queue có phải thứ quan trọng nhất trong microservices không?

Những câu hỏi này đều đúng, nhưng nếu trả lời riêng lẻ thì rất dễ lạc. Ví dụ, Kafka không thể được hiểu đúng nếu chưa hiểu queue, Pub/Sub, event log, replay và data pipeline. Kubernetes cũng không nên học như một "công nghệ deploy", mà phải đặt trong bài toán orchestration, scaling, self-healing và vận hành nhiều container.

Bản đồ giúp ta biết:

  • Thành phần này thuộc tầng nào của hệ thống.
  • Nó giải quyết vấn đề gì.
  • Nó thay thế hay bổ sung cho thành phần nào.
  • Khi nào nó là cần thiết.
  • Khi nào nó là quá mức.

Một hệ thống tốt thường không phải hệ thống dùng nhiều công nghệ nhất, mà là hệ thống dùng vừa đủ công nghệ cho áp lực hiện tại, đồng thời không tự khóa đường tiến hóa sau này.

---

0.2. Hệ thống hiện đại gồm những tầng nào?

Có thể nhìn một hệ thống web hiện đại qua các tầng sau.

Tầng người dùng

Đây là nơi request bắt đầu:

  • Browser
  • Mobile app
  • Desktop app
  • Third-party client
  • Bot hoặc automated client

Tầng này quan tâm đến trải nghiệm người dùng: trang có tải nhanh không, thao tác có phản hồi không, realtime có mượt không, lỗi có được hiển thị dễ hiểu không.

Tầng biên

Đây là lớp đứng giữa internet và hệ thống backend:

  • DNS
  • CDN
  • Firewall
  • WAF
  • Reverse proxy
  • Load balancer

Tầng biên giúp hệ thống nhận request an toàn và hiệu quả hơn. Nó có thể xử lý TLS, chặn request độc hại, cache file tĩnh, phân phối request đến nhiều server, hoặc bảo vệ origin server khỏi traffic quá lớn.

Tầng giao tiếp

Đây là cách các thành phần nói chuyện với nhau:

  • HTTP API
  • REST
  • GraphQL
  • gRPC
  • Webhook
  • Polling
  • SSE
  • WebSocket
  • Message queue
  • Pub/Sub
  • Event streaming

Chọn sai cách giao tiếp là một trong những nguyên nhân phổ biến làm hệ thống chậm hoặc khó debug. Một request cần trả lời ngay có thể dùng API. Một tác vụ dài nên đi qua queue. Một cập nhật realtime có thể dùng WebSocket hoặc SSE. Một dòng sự kiện cần lưu lại và replay có thể cần Kafka hoặc hệ event streaming tương tự.

Tầng ứng dụng

Đây là nơi chứa logic nghiệp vụ:

  • Monolith
  • Modular monolith
  • Microservices
  • Backend service
  • API service
  • Internal service

Đây là tầng nhiều người hay tranh luận nhất, nhưng cũng dễ bị hiểu sai nhất. Microservices không làm hệ thống tự động tốt hơn. Nó chỉ có ích khi ta thật sự cần tách deploy, tách scale, tách failure boundary, tách team hoặc tách quyền sở hữu dữ liệu.

Tầng xử lý nền

Không phải việc gì cũng nên xử lý trong request người dùng. Các việc lâu hoặc dễ lỗi nên được đưa ra nền:

  • Queue
  • Worker
  • Scheduler
  • Cron job
  • Workflow engine
  • Batch job

Ví dụ: gửi email, xử lý video, import file lớn, generate report, gọi API AI mất lâu, đồng bộ dữ liệu sang hệ thống khác.

Tầng này giúp request trả nhanh hơn, hệ thống chịu tải tốt hơn, và lỗi tạm thời có thể được retry.

Tầng dữ liệu

Đây là nơi hệ thống lưu và truy xuất dữ liệu:

  • Relational database
  • NoSQL database
  • Cache
  • Search engine
  • Object storage
  • Data warehouse
  • Data lake

Không nên xem mọi nơi lưu dữ liệu là "database giống nhau". PostgreSQL/MySQL phù hợp dữ liệu nghiệp vụ cần đúng. Redis phù hợp cache, counter, lock hoặc queue nhẹ. Elasticsearch/OpenSearch phù hợp tìm kiếm. Object storage phù hợp file lớn. Warehouse phù hợp phân tích.

Tầng vận hành

Đây là cách hệ thống được chạy và triển khai:

  • Linux server
  • Docker
  • Docker Compose
  • CI/CD
  • Kubernetes
  • Cloud service
  • Managed database
  • Managed queue

Tầng vận hành quyết định hệ thống có dễ deploy, rollback, scale, restart và phục hồi sau lỗi hay không.

Tầng quan sát

Không đo thì chỉ đoán. Tầng quan sát gồm:

  • Logs
  • Metrics
  • Traces
  • Alerts
  • Dashboards

Nếu không có quan sát, mọi cuộc tranh luận kiến trúc sẽ dễ biến thành cảm tính: "tôi nghĩ database chậm", "tôi nghĩ framework này yếu", "tôi nghĩ phải tách service". Một hệ thống tốt phải cho ta thấy nó đang nghẽn ở đâu.

Tầng bảo vệ

Tầng này giữ hệ thống an toàn:

  • Authentication
  • Authorization
  • Secret management
  • Audit log
  • Rate limiting
  • Network boundary
  • Encryption

Bảo mật không chỉ là đăng nhập. Nó còn là ai được xem dữ liệu nào, service nào được gọi service nào, API key nằm ở đâu, file upload có nguy hiểm không, request lạ có bị giới hạn không.

---

0.3. Một request đi qua bản đồ như thế nào?

Hãy hình dung một request đơn giản: người dùng mở một trang trong ứng dụng.

Luồng có thể diễn ra như sau:

1. Browser gửi request. 2. DNS phân giải domain thành địa chỉ hệ thống. 3. CDN có thể trả file tĩnh ngay nếu đã cache. 4. Request đi qua firewall/WAF để lọc traffic nguy hiểm. 5. Reverse proxy hoặc load balancer nhận request. 6. Request được chuyển đến backend phù hợp. 7. Backend xác thực người dùng. 8. Backend đọc cache nếu có. 9. Nếu cache không có, backend query database. 10. Backend có thể gọi service khác nếu cần. 11. Backend trả response. 12. Logs, metrics, traces ghi lại toàn bộ đường đi.

Nếu request này kích hoạt một việc lâu, ví dụ xử lý file hoặc gọi API ngoài mất nhiều thời gian, backend không nên đứng chờ trong request chính. Nó nên:

1. Lưu trạng thái ban đầu vào database. 2. Đẩy job vào queue. 3. Trả về cho người dùng rằng hệ thống đã nhận việc. 4. Worker xử lý job ở nền. 5. Khi xong, worker lưu kết quả. 6. Frontend biết kết quả qua polling, SSE, WebSocket hoặc notification.

Điểm quan trọng: cùng là "xử lý một hành động", nhưng hệ thống có thể chọn trả lời ngay hoặc xử lý sau. Kiến trúc tốt nằm ở chỗ biết phân biệt hai loại việc này.

---

0.4. Ba loại việc trong hệ thống

Phần lớn quyết định kiến trúc trở nên dễ hơn nếu ta phân loại việc trong hệ thống thành ba nhóm.

Việc cần trả lời ngay

Ví dụ:

  • Đăng nhập
  • Lấy thông tin user
  • Xem danh sách sản phẩm
  • Mở trang chi tiết
  • Kiểm tra quyền truy cập

Loại việc này thường dùng API trực tiếp. Nó cần latency thấp, timeout rõ ràng, và không nên phụ thuộc vào tác vụ lâu.

Việc có thể xử lý sau

Ví dụ:

  • Gửi email
  • Tạo báo cáo PDF
  • Xử lý video
  • Transcribe audio
  • Import dữ liệu lớn
  • Gọi API AI mất lâu

Loại việc này nên dùng queue và worker. Người dùng không nhất thiết phải chờ kết quả ngay trong HTTP request.

Việc cần phát cho nhiều nơi biết

Ví dụ:

  • Đơn hàng đã thanh toán
  • User vừa đăng ký
  • File đã xử lý xong
  • Bài chấm đã hoàn tất
  • Có event hành vi cần đưa vào analytics

Loại việc này có thể dùng event, Pub/Sub hoặc event streaming. Một event có thể được nhiều consumer xử lý: gửi notification, cập nhật dashboard, ghi analytics, đồng bộ search index.

Sai lầm phổ biến là dùng một kiểu giao tiếp cho mọi loại việc. Ví dụ, bắt HTTP request chờ một việc 90 giây, hoặc dùng Pub/Sub không bền cho job quan trọng, hoặc đưa Kafka vào chỉ để gửi vài email.

---

0.5. Bản đồ chọn công cụ nhanh

Đây là bản đồ ngắn để định hướng ban đầu.

Cần trả lời ngay

Dùng:

  • HTTP API
  • REST
  • GraphQL
  • gRPC nếu cần contract chặt và performance cao

Tránh:

  • Chờ tác vụ dài trong request
  • Retry vô hạn
  • Không có timeout

Việc lâu

Dùng:

  • Message queue
  • Worker
  • Background job
  • Workflow engine nếu luồng nhiều bước và phức tạp

Tránh:

  • Xử lý trong web request
  • Không lưu trạng thái job
  • Retry nhưng không idempotent

Cần realtime

Dùng:

  • Polling nếu đơn giản và tải thấp
  • SSE nếu server chỉ cần đẩy một chiều
  • WebSocket nếu cần hai chiều realtime

Tránh:

  • WebSocket cho mọi thứ chỉ vì nghe hiện đại
  • Không có chiến lược scale connection

Nhiều bên cùng cần biết một sự kiện

Dùng:

  • Pub/Sub
  • Event bus
  • Message broker có durable subscription nếu event quan trọng

Tránh:

  • Import trực tiếp module của nhau quá sâu
  • Gọi dây chuyền nhiều service trong cùng một request

Cần lưu lịch sử sự kiện và replay

Dùng:

  • Kafka
  • Event streaming platform

Tránh:

  • Dùng Kafka quá sớm khi chỉ cần queue
  • Không hiểu partition, offset, retention, consumer group

Cần đọc nhanh

Dùng:

  • Cache
  • CDN
  • Read replica trong một số trường hợp

Tránh:

  • Cache dữ liệu mà không biết khi nào xóa
  • Xem cache là source of truth

Cần tìm kiếm tốt

Dùng:

  • Search engine như Elasticsearch/OpenSearch

Tránh:

  • Ép database làm full-text search phức tạp khi dữ liệu lớn
  • Quên rằng search index có thể chậm đồng bộ

Cần giao file lớn

Dùng:

  • Object storage
  • CDN
  • Presigned URL

Tránh:

  • Đẩy file lớn qua web server nếu không cần
  • Lưu file lớn trong database

---

0.6. Bản đồ tiến hóa kiến trúc

Kiến trúc nên tiến hóa theo áp lực thật. Không nên bắt đầu bằng kiến trúc của công ty có hàng trăm triệu người dùng nếu sản phẩm còn chưa có người dùng đầu tiên.

Giai đoạn 1: Monolith rõ ràng

Đặc điểm:

  • Một backend chính
  • Một database chính
  • Deploy đơn giản
  • Ít service
  • Ít hạ tầng

Mục tiêu:

  • Ra sản phẩm nhanh
  • Hiểu domain
  • Tìm người dùng thật
  • Giữ code đủ sạch để không tự phá mình

Giai đoạn 2: Modular monolith

Đặc điểm:

  • Vẫn deploy chung
  • Code chia module rõ hơn
  • Có service layer
  • Có job queue
  • Có cache ở vài điểm cần thiết
  • Có logs/metrics cơ bản

Mục tiêu:

  • Kiểm soát độ phức tạp
  • Tách logic theo nghiệp vụ
  • Chuẩn bị khả năng tách service sau này

Giai đoạn 3: Tách service có chọn lọc

Đặc điểm:

  • Một vài phần được tách ra vì có lý do rõ ràng
  • Ví dụ: xử lý AI, xử lý video, notification, search
  • Có message queue hoặc API contract rõ
  • Có monitoring tốt hơn

Mục tiêu:

  • Tách đúng chỗ có áp lực thật
  • Không biến hệ thống thành distributed monolith

Giai đoạn 4: Event-driven và data pipeline

Đặc điểm:

  • Nhiều sự kiện nghiệp vụ quan trọng
  • Analytics bắt đầu quan trọng
  • Search, notification, recommendation, dashboard cần dữ liệu riêng
  • Có thể cần event streaming

Mục tiêu:

  • Tách hệ thống vận hành khỏi hệ thống phân tích
  • Cho nhiều consumer xử lý cùng một event
  • Hỗ trợ replay/backfill khi cần

Giai đoạn 5: Platform hóa

Đặc điểm:

  • Nhiều service
  • Nhiều team
  • Hạ tầng managed hoặc Kubernetes
  • Observability nghiêm túc
  • SLO/SLA rõ ràng
  • Security và compliance cao hơn

Mục tiêu:

  • Vận hành hệ thống lớn ổn định
  • Tự động hóa deploy, scale, rollback
  • Giảm chi phí phối hợp giữa các team

Nguyên tắc: không nhảy giai đoạn vì thích công nghệ. Hãy để áp lực thật kéo kiến trúc đi lên.

---

0.7. Bản đồ đọc lỗi hệ thống

Khi hệ thống có vấn đề, không nên đoán ngay "phải đổi framework" hay "phải dùng microservices". Hãy đọc triệu chứng.

Chậm một request

Nên xem:

  • Trace của request
  • Query database
  • External API
  • Cache hit/miss
  • Serialization
  • Network timeout

Chậm toàn hệ thống

Nên xem:

  • CPU
  • RAM
  • Database connection pool
  • Load balancer
  • Worker saturation
  • Queue backlog
  • External provider latency

Job chờ lâu

Nên xem:

  • Queue length
  • Queue age
  • Worker concurrency
  • Worker error rate
  • Provider latency
  • Rate limit
  • Retry count

Dữ liệu sai

Nên xem:

  • Transaction boundary
  • Race condition
  • Duplicate retry
  • Idempotency
  • Event ordering
  • Cache invalidation
  • Manual data fix

Service chết dây chuyền

Nên xem:

  • Timeout
  • Retry storm
  • Circuit breaker
  • Bulkhead
  • Connection pool
  • Dependency health
  • Cascading failure

Khó debug

Nên xem:

  • Structured logs
  • Correlation ID
  • Distributed tracing
  • Metrics theo từng service
  • Dashboard cho queue/database/API
  • Log có bị thiếu context không

Một người làm hệ thống giỏi không chỉ biết xây, mà còn biết đọc tín hiệu khi hệ thống đau.

---

0.8. Những nguyên tắc xuyên suốt cuốn sách

Đơn giản trước, phức tạp sau

Nếu monolith giải quyết được, chưa cần microservices. Nếu PostgreSQL giải quyết được, chưa cần NoSQL. Nếu Redis/RabbitMQ đủ, chưa cần Kafka. Nếu Docker Compose/PaaS đủ, chưa cần Kubernetes.

Đơn giản không có nghĩa là sơ sài. Đơn giản là ít thành phần nhất nhưng vẫn đúng yêu cầu.

Đo trước, tối ưu sau

Không có số liệu, mọi quyết định dễ thành cảm tính. Trước khi đổi kiến trúc, hãy đo latency, throughput, concurrency, queue age, error rate, CPU, RAM, database query, provider latency.

Tách logic trước, tách service sau

Nhiều hệ thống thất bại vì tách server quá sớm trong khi code vẫn dính chặt. Hãy chia module, service layer, domain boundary trước. Khi cần tách runtime, việc tách sẽ ít đau hơn.

Timeout mọi thứ có thể chờ

Mọi call ra ngoài đều phải có timeout: database, API ngoài, service khác, queue operation, file operation. Không có timeout là để một lỗi nhỏ giữ tài nguyên vô hạn.

Retry phải đi cùng idempotency

Retry giúp hệ thống phục hồi lỗi tạm thời, nhưng retry sai có thể tạo đơn hàng trùng, gửi email trùng, cộng tiền trùng, ghi điểm trùng. Muốn retry an toàn phải thiết kế idempotency.

Queue giúp chống nghẽn nhưng làm hệ thống bất đồng bộ hơn

Queue rất mạnh, nhưng nó đổi bài toán từ "xử lý ngay" thành "xử lý sau". Điều này kéo theo job state, retry, dead letter, monitoring, eventual consistency và UI trạng thái chờ.

Cache giúp nhanh hơn nhưng có thể làm sai dữ liệu

Cache không phải miễn phí. Dữ liệu cache có thể cũ, sai, khó invalidate. Chỉ cache khi biết rõ dữ liệu nào được phép cũ và cũ trong bao lâu.

Microservices tăng tự do nhưng cũng tăng chi phí vận hành

Microservices giúp tách deploy, tách scale, tách lỗi và tách team. Nhưng nó thêm network, distributed data, observability, security, contract testing và vận hành phức tạp. Nếu chưa cần những lợi ích đó, đừng vội trả cái giá đó.

Không có công nghệ tốt nhất

Chỉ có lựa chọn phù hợp nhất với vấn đề, giai đoạn, team, tải, ngân sách và khả năng vận hành.

---

0.9. Kết luận của chương

Tấm bản đồ chung không giúp ta biết hết mọi thứ ngay lập tức, nhưng nó giúp ta không học lạc. Khi gặp một công nghệ mới, ta sẽ hỏi:

  • Nó nằm ở tầng nào?
  • Nó giải quyết vấn đề gì?
  • Nó thay thế hay bổ sung cho thứ gì?
  • Nó có làm hệ thống đơn giản hơn không?
  • Nếu dùng sai, nó gây ra loại đau nào?

Đó là cách học kiến trúc thực dụng: không chạy theo tên công nghệ, mà học cách nhìn hệ thống như một tập hợp các vấn đề, áp lực và trade-off.