Level 2 – Engineering · Yêu cầu: đã hoàn thành Level 1.


1. Problem Statement: câu chuyện lặp lại của mọi hệ thống

Robert C. Martin công bố bài viết “The Clean Architecture” năm 2012, đúc kết từ hàng thập kỷ quan sát một vòng đời lặp đi lặp lại:

Năm 1: team chọn framework hot nhất (Rails/Spring/Django — ngày nay có thể là một “boilerplate Gin + GORM”). Framework quyết định cấu trúc project. Tốc độ ban đầu tuyệt vời — CRUD sinh ra trong vài giờ.

Năm 2: nghiệp vụ phức tạp dần. Logic không còn khớp “1 API = 1 bảng”. Business rule bắt đầu vương vãi: một ít trong controller, một ít trong model callback/ORM hook, một ít trong stored procedure, một ít trong job queue.

Năm 3–4: không ai trả lời được câu “rule tính giá nằm ở đâu?” bằng một tên file. Test suite chậm vì mọi test cần DB. Upgrade framework major version bị hoãn vô thời hạn vì đụng đâu vỡ đó. Ý tưởng “viết lại từ đầu” xuất hiện — và thường tạo ra hệ thống thứ hai với cùng số phận.

Martin gọi đúng tên căn bệnh: hệ thống bị định hình bởi công nghệ thay vì bởi nghiệp vụ. Mở repo ra thấy “đây là một app Gin” chứ không thấy “đây là hệ thống quản lý đơn hàng”. Ông đề xuất bài kiểm tra nổi tiếng — screaming architecture: kiến trúc phải “gào lên” nghiệp vụ của nó, như bản vẽ nhà thờ phải nhìn ra ngay là nhà thờ.

Nếu không có gì thay đổi thì điều gì xảy ra?

Bệnh này không tự khỏi, vì nó có cấu trúc khuyến khích (incentive structure) tự củng cố: mỗi deadline đẩy thêm một ít logic vào chỗ tiện nhất (handler, ORM hook); mỗi lần như vậy chi phí làm đúng lần sau lại cao hơn. Kết cục thống kê được trong ngành: rewrite trung bình mỗi 4–7 năm, với chi phí lớn hơn nhiều tổng chi phí thiết kế đúng từ đầu.

Các cách tiếp cận trước đó và hạn chế

Clean Architecture không phải ý tưởng mới. Nó là bản tổng hợp của một dòng tư tưởng hội tụ:

NămKiến trúcĐóng gópCòn thiếu
~1970s–90sLayered (N-tier)Tách concern theo tầngPhụ thuộc vẫn chảy xuống DB; business dính hạ tầng
2005Hexagonal / Ports & Adapters (Alistair Cockburn)Insight then chốt: ứng dụng ở tâm, mọi thứ bên ngoài (kể cả DB!) là adapter cắm vào portKhông nói gì về cấu trúc bên trong hình lục giác
2008Onion (Jeffrey Palermo)Chia phần trong thành các vòng: Domain Model ở lõi, Domain Services, Application Services bọc quanhRanh giới các vòng còn mô tả lỏng
2003–DDD (Eric Evans)Ngôn ngữ mô hình hóa phần lõi: Entity, Value Object, Aggregate, Bounded ContextKhông quy định quan hệ với hạ tầng
2012Clean Architecture (Martin)Hợp nhất tất cả bằng MỘT quy tắc: Dependency Rule + đặt tên các vòng

Điểm chung của cả bốn tiền bối, theo lời chính Martin: “chúng đều tạo ra hệ thống độc lập với framework, testable, độc lập với UI, độc lập với database, độc lập với mọi tác nhân bên ngoài”. Clean Architecture là nỗ lực phát biểu cái lõi chung đó gọn nhất có thể.

Hiểu điều này giúp bạn tránh cuộc tranh cãi vô bổ “Hexagonal hay Clean hay Onion?” — chúng là một ý tưởng, ba cách vẽ (chương 13 so sánh chi tiết).


2. Tại sao giải pháp này tồn tại

Business Problem. Doanh nghiệp mua phần mềm để mã hóa cách họ vận hành. Cách vận hành đó (business rules) là tài sản; Postgres, Gin, Kafka là phương tiện thuê ngoài có vòng đời ngắn hơn. Bài toán: làm sao để tài sản không bị chôn lẫn vào phương tiện, để 10 năm sau doanh nghiệp vẫn sở hữu một mô hình nghiệp vụ đọc được, test được, chuyển được sang công nghệ mới.

Technical Problem. Ba yêu cầu kỹ thuật cụ thể: (a) test business rule trong mili-giây, không cần hạ tầng; (b) trì hoãn và đảo ngược được các quyết định công nghệ — Martin: “kiến trúc sư giỏi tối đa hóa số quyết định CHƯA phải đưa ra”; (c) nhiều delivery mechanism (HTTP, gRPC, CLI, consumer) dùng chung một bộ nghiệp vụ.

Design Problem. Layered Architecture thất bại vì thiếu một quy tắc cưỡng chế về hướng. Clean Architecture bổ sung đúng một câu: source code dependency chỉ được trỏ vào trong, về phía chính sách cấp cao hơn. Mọi thứ khác — số vòng, tên vòng, folder — chỉ là trang trí quanh câu đó.


3. Giải thích bản chất: Clean Architecture thực sự nói gì

Tước bỏ hình vẽ bốn vòng tròn màu, Clean Architecture còn lại ba mệnh đề:

Mệnh đề 1 — Phân tầng theo mức độ chính sách (policy level). Code càng gần “quyết định nghiệp vụ thuần túy” thì cấp càng cao; càng gần “cơ chế vào/ra dữ liệu” thì cấp càng thấp. Đây là tiêu chí xếp tầng khách quan: hỏi “dòng code này có còn đúng nếu đổi từ HTTP sang gRPC, từ Postgres sang Mongo?” — nếu có, nó thuộc tầng cao.

Mệnh đề 2 — Dependency Rule. Phụ thuộc mã nguồn chỉ trỏ từ cấp thấp lên cấp cao (từ vòng ngoài vào vòng trong). Vòng trong không được biết tên của bất cứ thứ gì vòng ngoài — không import, không kiểu dữ liệu, không hằng số, không format.

Mệnh đề 3 — Vượt ranh giới bằng DIP. Khi control flow cần chạy từ trong ra ngoài (use case cần lưu DB), dùng cơ chế đã học ở chương 1.3: vòng trong khai báo interface, vòng ngoài implement.

Bạn nhận ra ngay: cả ba mệnh đề đều đã được chứng minh ở Level 1. Mệnh đề 1 là Stable Dependencies (1.1) + SoC theo trục kỹ thuật (1.4). Mệnh đề 2 là “phụ thuộc trỏ về phía ổn định” phát biểu thành luật. Mệnh đề 3 là DIP (1.3). Clean Architecture không phát minh gì — nó hệ thống hóa các nguyên lý và cho chúng một hình vẽ dễ nhớ. Đây cũng là lý do những người học Clean Architecture bằng cách copy template folder đều thất bại: họ copy hình vẽ mà thiếu ba mệnh đề.

Nó bảo vệ điều gì

  • Tài sản nghiệp vụ: business rule tập trung, thuần túy, đọc được như tài liệu đặc tả.
  • Quyền đổi ý: mọi công nghệ đều là chi tiết thay được ở vòng ngoài.
  • Tốc độ dài hạn: chi phí thay đổi tỉ lệ với kích thước thay đổi nghiệp vụ, không tỉ lệ với kích thước codebase.

Nó KHÔNG hứa điều gì (để không thần thánh hóa)

  • Không làm code ít hơn — thường nhiều hơn 20–40% (adapter, DTO mapping, wiring).
  • Không thay thế thiết kế domain tốt: Clean Architecture với domain model rỗng (anemic) chỉ là bộ khung đắt tiền bọc CRUD.
  • Không tự động phù hợp mọi dự án — với CRUD đơn giản, nó là bảo hiểm mua cho rủi ro không tồn tại.
  • Việc “đổi database dễ dàng” hiếm khi xảy ra thật; giá trị thường ngày nằm ở testability và khả năng khoanh vùng thay đổi, không phải ở kịch bản swap công nghệ.

4. Bốn vòng tròn — đọc đúng cách

        ┌──────────────────────────────────────────────────┐
        │  Frameworks & Drivers (vòng 4 — chi tiết)         │
        │  Web framework, DB driver, Kafka client, UI       │
        │   ┌────────────────────────────────────────┐      │
        │   │  Interface Adapters (vòng 3)            │      │
        │   │  Controller, Presenter, Gateway/Repo    │      │
        │   │   ┌──────────────────────────────┐      │      │
        │   │   │  Use Cases (vòng 2)           │      │      │
        │   │   │  Application Business Rules   │      │      │
        │   │   │   ┌────────────────────┐      │      │      │
        │   │   │   │  Entities (vòng 1)  │      │      │      │
        │   │   │   │  Enterprise Rules   │      │      │      │
        │   │   │   └────────────────────┘      │      │      │
        │   │   └──────────────────────────────┘      │      │
        │   └────────────────────────────────────────┘      │
        └──────────────────────────────────────────────────┘
                  Mọi phụ thuộc trỏ VÀO TRONG ──▶ ◀──

Ba hiểu lầm phổ biến cần dập ngay:

  1. “Phải có đúng 4 tầng.” Martin viết rõ: số vòng là ví dụ minh họa. Quy tắc duy nhất là Dependency Rule. Service Go điển hình thường chỉ cần 3 (domain, usecase, adapter) — thậm chí domain và usecase gộp một package khi nghiệp vụ nhỏ.
  2. “Mỗi vòng = một folder.” Vòng là khái niệm về mức chính sách, ánh xạ sang package theo nhiều cách (chương 3). Folder đúng tên mà import sai hướng vẫn là vi phạm; folder “sai tên” mà mũi tên đúng vẫn là Clean Architecture.
  3. “Dữ liệu phải copy qua từng tầng.” Dependency Rule cấm vòng trong biết kiểu của vòng ngoài, không cấm vòng ngoài dùng kiểu của vòng trong. Handler dùng order.Order là hợp lệ; use case nhận *gin.Context là vi phạm. DTO chỉ bắt buộc tại các ranh giới cần cách ly format (API công khai, message schema).

Chi tiết từng vòng và cách vượt ranh giới: hai chương tiếp theo.


5. Trade-off tổng quan (chi tiết rải trong toàn tài liệu)

Bạn trảBạn nhận
Nhiều file hơn, indirection nhiều hơnBusiness rule tập trung, test mili-giây
Đường cong học cho teamNgôn ngữ thiết kế chung, PR review có tiêu chí khách quan
Mapping DTO ↔ domain ở ranh giớiRanh giới chống vỡ khi format ngoài thay đổi
Kỷ luật liên tục (linter, review)Kiến trúc không mục nát theo thời gian
Chậm hơn ~10–20% ở tháng đầuVelocity ổn định ở năm thứ 3 thay vì tiệm cận 0

Điều kiện hòa vốn: hệ thống sống ≥ 1–2 năm, ≥ 2 người maintain, nghiệp vụ có rule thật (không chỉ CRUD). Không đạt điều kiện → xem mục “Khi nào không nên dùng” ở các chương sau.


Tóm tắt chương

  • Clean Architecture ra đời để chữa căn bệnh “hệ thống bị công nghệ định hình”, tổng hợp Hexagonal + Onion + DDD + Layered thành một quy tắc duy nhất.
  • Bản chất = 3 mệnh đề: xếp tầng theo mức chính sách, phụ thuộc trỏ vào trong, vượt ranh giới bằng DIP — tất cả đã được chứng minh từ Level 1.
  • Hình vẽ 4 vòng là minh họa, không phải luật; luật duy nhất là Dependency Rule.

Chương tiếp theo: Dependency Rule — luật duy nhất và cách cưỡng chế nó