Clean Architecture
01 Phần I · Nền tảng Chương 1 & 2

Thiết kế, Kiến trúc & Hai giá trị

Design, Architecture & A Tale of Two Values

Mục tiêu của kiến trúc phần mềm rất tỉnh táo: tối thiểu hóa nhân lực cần để xây dựng và bảo trì hệ thống. Mọi chương sau của cuốn sách chỉ là cách đạt mục tiêu đó — bắt đầu bằng việc hiểu vì sao "code ẩu để ra mắt cho nhanh" luôn là một canh bạc thua.

01 Mục tiêu của kiến trúc

Mục tiêu của kiến trúc phần mềm là tối thiểu hóa nhân lực cần để xây dựng và bảo trì hệ thống.

Robert C. Martin

Kiến trúc không phải là những chiếc hộp đẹp trên slide. Nó là một mục tiêu đo được: nỗ lực (effort) bỏ ra để đáp ứng nhu cầu khách hàng. Nỗ lực thấp và giữ ở mức thấp suốt vòng đời ⟶ thiết kế tốt. Nỗ lực phình to sau mỗi bản phát hành ⟶ thiết kế tồi. Đơn giản vậy thôi.

Thước đo chất lượng thiết kế (measure of design quality) = nỗ lực để đáp ứng nhu cầu khách hàng. Không phải số dòng code, không phải số tính năng — mà là chi phí của thay đổi tiếp theo.

Design vs Architecture: không có ranh giới

Người ta hay tách "architecture" (cấp cao, trừu tượng) khỏi "design" (chi tiết cấp thấp). Martin khẳng định: không có khác biệt nào cả. Chỉ có một thể liên tục các quyết định (continuum of decisions) từ cao nhất tới thấp nhất — và chi tiết cấp thấp cũng quan trọng y như cấu trúc cấp cao.

flowchart LR
  A["Quyết định cấp cao
(cấu trúc hệ thống)"]:::hi --> B["…"]:::mid --> C["…"]:::mid --> D["Chi tiết cấp thấp
(tên biến, một hàm)"]:::lo classDef hi fill:#dbeee8,stroke:#0f7d72,color:#1c1a14; classDef mid fill:#eef4f1,stroke:#8c8675,color:#1c1a14; classDef lo fill:#e2edf3,stroke:#2f6d93,color:#1c1a14;
Một dải liên tục, không có lằn ranh "đây là kiến trúc, kia chỉ là thiết kế". Người làm kiến trúc vẫn phải nhìn thấy mọi chi tiết nhỏ chống đỡ các quyết định lớn.

02 Dấu hiệu của một mớ hỗn độn

Khi hệ thống bị quăng ra cho kịp tiến độ, khi số lượng lập trình viên là động lực duy nhất của sản lượng, và không ai bận tâm tới độ sạch của code — bạn sẽ cưỡi đúng đường cong dưới đây tới hồi kết xấu xí. Đây là dữ liệu thật từ một công ty thật (the signature of a mess).

~100% → 0% Release 1 → 8 → Năng suất (Productivity)
Productivity by release: khởi đầu gần 100%, rồi tiệm cận 0 (asymptotic approach to zero) — dù không ai giảm nỗ lực. Mọi công sức bị chuyển từ làm tính năng sang… dọn mớ hỗn độn.

Nhân sự ↑

engineering staff

Tăng vọt qua các giai đoạn, lên tới ~1.200 người ở release 8.

Chi phí/dòng ↑

cost per line of code

Ở release 8, mỗi dòng code đắt gấp 40 lần so với release 1.

Lương tháng → vô ích

monthly payroll

Quỹ lương chạm 20 triệu USD/tháng nhưng gần như không mua thêm được tính năng nào.

Bất kỳ CFO nào nhìn hai đường "lương tăng vọt" và "sản lượng đi ngang" cũng biết: phải hành động ngay, nếu không các đường cong này sẽ rút cạn lợi nhuận và đẩy công ty tới chỗ đình trệ.

03 Rùa, Thỏ & lời nói dối quen thuộc

Vì sao năng suất sụp đổ? Vì lập trình viên hiện đại chạy đua với một sự tự tin thái quá (overconfidence) giống hệt chú Thỏ. Họ không lười — họ làm quần quật — nhưng phần não biết rằng code sạch mới quan trọng thì đang ngủ.

"Cứ làm cho kịp ra mắt đã, dọn dẹp sau cũng được!" — Tất nhiên, chẳng bao giờ có cái "sau" đó, vì áp lực thị trường không bao giờ ngơi nghỉ.

The familiar lie · Clean Architecture

Niềm tin của chú Thỏ

  • "Code ẩu giúp đi nhanh hơn."
  • "Ra thị trường trước đã, dọn sau."
  • "Viết lại từ đầu sẽ tốt hơn." — vẫn là chú Thỏ đang nói.

Sự thật của chú Rùa

  • Tạo mớ hỗn độn luôn CHẬM hơn giữ code sạch — ở mọi thời điểm.
  • Cách duy nhất để đi nhanh là làm cho tốt (the only way to go fast is to go well).
  • Thí nghiệm của Jason Gorman: kỷ luật TDD nhanh hơn ~10%, ngày TDD chậm nhất vẫn nhanh hơn ngày không-TDD nhanh nhất.

Cái bẫy "viết lại từ đầu" cũng chính là chú Thỏ: cùng một sự tự tin đã tạo ra mớ hỗn độn giờ lại thì thầm rằng "lần này tôi sẽ làm tốt hơn nếu được bắt đầu lại". Thực tế ít màu hồng hơn nhiều.

04 Hai giá trị: Hành vi vs Cấu trúc

Mọi hệ thống đều mang lại cho stakeholder hai giá trị khác nhau. Bi kịch là lập trình viên thường dồn sức cho cái kém quan trọng hơn, khiến phần mềm cuối cùng… mất sạch giá trị.

Giá trị 1 · Hành vi

Behavior

Làm máy chạy đúng yêu cầu (requirements) để tạo ra hoặc tiết kiệm tiền. Đây là việc đa số lập trình viên nghĩ là "toàn bộ công việc" — và họ nhầm.

Giá trị 2 · Cấu trúc

Architecture / Structure

Phần mềm gọi là "soft"-ware vì nó sinh ra để dễ thay đổi. Nếu khó đổi hành vi, lẽ ra nên gọi nó là "hardware".

Vì sao Cấu trúc quan trọng hơn về dài hạn

Tình huốngHệ quảCòn cứu được?
Chạy đúng nhưng không thể sửaKhi yêu cầu đổi, không theo kịp → trở nên vô dụng.không
Chạy chưa đúng nhưng dễ sửaCó thể sửa cho chạy đúng và tiếp tục hữu dụng.được

Độ khó của một thay đổi nên tỉ lệ với quy mô (scope) của nó, chứ không phải hình dạng (shape). Khi chi phí phình theo shape, đó là lúc kiến trúc tồi bắt đầu rút tiền — năm thứ hai đắt hơn năm đầu, năm thứ ba đắt hơn nữa.

"soft"-ware qua lăng kính RAG

RAG  Phụ thuộc nhà cung cấp LLM nằm rải khắp pipeline = "hardware" (đổi shape là sửa khắp nơi). Đẩy nó sau một cổng trừu tượng = đổi nhà cung cấp chỉ còn là thay đổi theo scope:

Python · "hardware" ✗ vs "soft"-ware ✓
# ✗ Hành vi đúng, nhưng cứng — đổi LLM = sửa hàng chục chỗ
def answer(q):
    ctx = retrieve(q)
    return openai.chat(model="gpt-4o", messages=build(ctx, q))   # gọi thẳng, rải khắp

# ✓ Dễ thay đổi — hành vi sau một cổng; đổi nhà cung cấp = 1 lớp
class LLM(Protocol):
    def generate(self, prompt: str) -> str: ...

def answer(q, llm: LLM):
    return llm.generate(build(retrieve(q), q))   # scope đổi, shape không đổi
Cùng một hành vi, nhưng bản dưới "mềm": khi stakeholder đổi ý (thay vendor, thử model mới), chi phí tỉ lệ với scope — đúng tinh thần giá trị thứ hai.

05 Ma trận Eisenhower & cuộc đấu tranh

Hành vi thì khẩn (urgent) nhưng không phải lúc nào cũng quan trọng. Kiến trúc thì luôn quan trọng (important) nhưng hiếm khi khẩn. Tổng thống Eisenhower: "Cái khẩn thì hiếm khi quan trọng, cái quan trọng thì hiếm khi khẩn."

KIẾN TRÚC quan trọng · không khẩn ưu tiên 2 quan trọng · khẩn ưu tiên 1 không quan trọng · không khẩn ưu tiên 4 HÀNH VI khẩn · không quan trọng ưu tiên 3 sai lầm: 3→1 Khẩn cấp (Urgency) → Quan trọng (Importance) →
Kiến trúc nằm ở ô quan trọng-không khẩn (ưu tiên 2). Sai lầm kinh điển của business manager & developer: kéo các tính năng ô 3 (khẩn nhưng không quan trọng) lên ưu tiên 1, bỏ rơi kiến trúc.

Đấu tranh cho kiến trúc

Business manager không đủ chuyên môn để đánh giá tầm quan trọng của kiến trúc — đó chính là việc bạn được thuê để làm. Vậy nên đội phát triển phải "đấu tranh cho kiến trúc" (fight for the architecture) như một stakeholder thực thụ, ngang hàng với marketing, sales, vận hành.

Đây là một cuộc vật lộn (struggle) — và đó là cách mọi việc luôn diễn ra. Nếu để kiến trúc xếp cuối, hệ thống sẽ sớm quá đắt để phát triển, thay đổi trở nên bất khả thi. Khi đó nghĩa là: đội phát triển đã không đấu tranh đủ mạnh cho điều họ biết là cần thiết.

06 Ghi nhớ nhanh

Mục tiêu kiến trúc = tối thiểu hóa nhân lực để xây & bảo trì. Thước đo = nỗ lực cho thay đổi tiếp theo, không phải số tính năng.

Cách duy nhất để đi nhanh là làm cho tốt. Tạo mớ hỗn độn luôn chậm hơn giữ code sạch — ở mọi thời điểm.

Giá trị thật của phần mềm nằm ở khả năng dễ thay đổi — chương trình chạy đúng mà không sửa được rồi sẽ vô dụng; chương trình dễ sửa thì còn cứu được.

Đừng để cái "khẩn nhưng không quan trọng" lấn cái "quan trọng nhưng không khẩn". Kiến trúc luôn ở nhóm quan trọng.

Bạn là một stakeholder. Hãy đấu tranh cho kiến trúc — nếu nó xếp cuối, hệ thống sẽ đắt tới mức không thể đổi.

NguồnChương 1 (What Is Design and Architecture?) & Chương 2 (A Tale of Two Values), Clean Architecture — Robert C. Martin, Prentice Hall 2017.