Files
sys-analysis-design/.claude/skills/sa-lifecycle/references/design-rules.md
Leonard-ThindPad-P50 c81f249920 init git
2026-09-08 10:26:21 +07:00

10 KiB
Raw Blame History

12 quy tắc viết tài liệu kiến trúc

Mọi skill sa-* phải tuân thủ. Đây là thứ phân biệt một tài liệu kiến trúc dev đọc xong thi công được với một bộ sơ đồ hộp và mũi tên.

Bộ này song song với W1–W12 của bộ BA (../../ba-lifecycle/references/writing-rules.md). Khi làm tài liệu chạm cả hai (ví dụ ICD đối chiếu API của BA), tuân thủ cả hai bộ.

D1 — Một ADR, một quyết định

❌ ADR-004: Chọn stack backend — bên trong quyết cả ngôn ngữ, framework, ORM, message broker. ✅ Bốn ADR. Vì sáu tháng sau khi đổi message broker, bạn cần supersede đúng một quyết định, không phải viết lại một tài liệu còn đúng ba phần tư.

Ngưỡng "quyết định nào cần ADR": xem decision-radar.md.

D2 — Cấm NFR định tính

Danh sách cấm, kèm cách thay:

Cấm Thay bằng
nhanh, phản hồi tốt p95 ≤ 300ms cho GET /orders tại 2.000 rps
chịu tải cao, mở rộng được 20.000 đơn/giờ giờ cao điểm, tăng 3×/năm trong 2 năm
ổn định, sẵn sàng cao 99.9%/tháng ⇒ ngân sách lỗi 43 phút/tháng
bảo mật tên mối đe doạ + biện pháp + cách kiểm chứng
dễ bảo trì một dev mới onboard và sửa được một bug trong ≤ 3 ngày
dữ liệu lớn con số + đơn vị + tốc độ tăng

Mỗi QAS phải trả lời đủ bốn câu: kịch bản gì · con số bao nhiêu · đo bằng cách nào · ai đo. Thiếu câu thứ ba là NFR không verify được ⇒ theo D8 nó chỉ là mong muốn.

Quét trước khi nộp: grep -niE "nhanh|ổn định|dễ (bảo trì|dùng)|chịu tải cao|bảo mật$" <file>

D3 — Không có phương án bị loại thì không phải quyết định

Mọi ADR và mọi OPT phải nêu ≥ 1 phương án đã cân nhắc và loại, kèm lý do loại nói được bằng ràng buộc (CON-nn) hoặc thuộc tính chất lượng (QAS-nnn), không phải bằng sở thích.

❌ "Chọn PostgreSQL vì team quen." ✅ "Chọn PostgreSQL. Loại MongoDB vì QAS-007 yêu cầu giao dịch nhiều bảng nguyên tử; loại MySQL vì CON-03 (đội vận hành chỉ có kinh nghiệm Postgres) và chênh lệch hiệu năng ở QAS-004 không đáng kể theo POC-02."

"Team quen" là một lý do hợp lệ — nhưng phải viết ra như một ràng buộc có tên, để sau này biết quyết định này gắn với con người chứ không phải với kỹ thuật.

D4 — Sơ đồ phải khai báo mức, và không trộn mức

Mỗi sơ đồ ghi rõ ở đầu: C4 mức nào (Context / Container / Component / Code) hoặc loại (deployment, sequence, state, data flow). Một sơ đồ có cả "trình duyệt người dùng" lẫn "class OrderValidator" là sơ đồ không ai đọc được.

Kèm mỗi sơ đồ: một legend giải thích hình khối và loại mũi tên (sync/async, ai gọi ai, giao thức). Mũi tên không nhãn không mang thông tin.

Vẽ bằng mermaid, không ASCII art — thống nhất với quy tắc W13 của bộ BA (../../ba-lifecycle/references/diagram-rules.md). GitHub/Confluence và Artifact của Claude Code render thẳng, và git diff đọc được từng cạnh.

Sơ đồ Loại mermaid Sinh ở
C4 Context / Container / Component flowchart + subgraph SAD §3–§5
Deployment view flowchart TB + subgraph theo vùng/AZ SAD §7, INF §3
Luồng chính, có nhánh lỗi sequenceDiagram SAD §6
Ranh giới tin cậy flowchart LR SEC §1
Mô hình dữ liệu khái niệm erDiagram DAT §2
Phụ thuộc giữa mục roadmap flowchart LR TRM §4
Phương án ở mức khối flowchart LR OPT §3

🔴 Mỗi sơ đồ phải có bảng đi kèm (W13). Sơ đồ để nhìn; bảng để truy vết, để đặt con số, và để kiểm chứng. Sơ đồ không diễn đạt được timeout, quyền, mã lỗi, hay ai sở hữu cái gì.

D5 — Mọi interface có chủ, có contract, có chính sách phiên bản

Mỗi dòng trong ICD phải có: IF-nnn · hai đầu · giao thức · sync hay async · ai sở hữu contract · nơi contract sống (đường dẫn file OpenAPI/AsyncAPI/proto) · chính sách đổi phiên bản · hành vi khi đầu kia hỏng.

🔴 Interface không biết ai sở hữu là interface sẽ bị đổi mà không ai báo.

D6 — Đường lỗi là bắt buộc cho mọi thứ vượt ranh giới process

Mọi lời gọi ra khỏi process (HTTP, DB, cache, queue, file, hệ thống ngoài) phải ghi: timeout · retry (số lần + backoff + có idempotent không) · hành vi khi hết retry · ảnh hưởng tới người dùng.

Bốn câu hỏi cho mỗi phụ thuộc, không được bỏ câu nào:

  1. Nó chậm thì sao? (không phải "hỏng" — chậm nguy hiểm hơn hỏng)
  2. Nó hỏng thì sao? Có degrade được không hay chết cả luồng?
  3. Nó trả sai dữ liệu thì sao? Có phát hiện được không?
  4. Nó hồi phục thì sao? Có retry storm không? Có cần backpressure không?

D7 — Mỗi mảnh dữ liệu có đúng một chủ sở hữu

Trong DAT, mỗi thực thể ghi rõ hệ thống nào là single source of truth. Mọi bản sao khác là read model, phải ghi: cập nhật bằng cơ chế gì, độ trễ tối đa bao nhiêu, và được phép lệch bao lâu trước khi coi là sự cố.

❌ "Cả hai hệ thống đều lưu thông tin khách hàng và đồng bộ hai chiều." ✅ Chọn một bên làm chủ. Đồng bộ hai chiều không có chủ là cách sinh ra dữ liệu mâu thuẫn không ai gỡ được.

D8 — Ràng buộc không verify tự động được là khuyến nghị, không phải ràng buộc

Mỗi ràng buộc kiến trúc trong AGD phải có một trong hai:

  • một fitness function trong FIT (ArchUnit, dependency-cruiser, lint rule, kiểm thử tải, kiểm tra hạ tầng) chạy trên CI, hoặc
  • ghi thẳng nhãn ⚠️ Khuyến nghị — không tự kiểm được.

Không có nhãn và không có bài kiểm ⇒ trong sáu tháng nó sẽ bị vi phạm và không ai biết.

D9 — Mọi con số hạ tầng phải quy được ra tiền và ra đơn vị

"Cần 8 node" ⇒ node loại gì, ở vùng nào, bao nhiêu tiền/tháng, ở mức tải nào. TCO và INF phải khớp nhau; lệch thì TCO sai hoặc INF sai, không có khả năng thứ ba.

Mọi con số ghi kèm nguồn: bài đo nào, POC nào, báo giá nào, ngày nào. Con số không nguồn là 🔴 giả định, phải hạ Confidence của tài liệu.

D10 — Không quyết định thay người có thẩm quyền

SA trình phương án kèm khuyến nghị. Ba loại quyết định không thuộc SA:

Loại Chủ SA làm gì
Trade-off nghiệp vụ (chậm hơn nhưng rẻ hơn) PO Trình bảng đánh đổi kèm con số
Chấp nhận rủi ro bảo mật Security Trình threat model + biện pháp + chi phí
Ngân sách, tiến độ, nhân sự PO / PM Trình TCO và effort

Gặp chỗ chưa rõ, viết:

> **OQ-021** — Chấp nhận eventual consistency ≤ 5 giây cho số dư ví không?
> **Hỏi:** PO (chị Lan) + Security · **Từ:** 2026-08-29 · **Chặn:** ADR-009, QAS-011
> **Phương án SA đề xuất:** Chấp nhận, vì … *(đề xuất, chưa phải quyết định)*
> **Nếu không chấp nhận:** phải dùng phân tán 2 pha ⇒ +6 tuần, +30% chi phí hạ tầng

Luôn ghi hệ quả của phương án ngược lại bằng con số — đó là thứ giúp người có thẩm quyền quyết được trong một lần đọc.

D11 — Ghi cả cái kiến trúc KHÔNG làm

Cuối mỗi tài liệu, hai mục bắt buộc:

  • Ngoài phạm vi — những thứ người đọc có thể tưởng là có: "Không hỗ trợ đa vùng (multi-region) trong phiên bản này; DR dựa trên khôi phục từ backup, RTO 4 giờ."
  • Giả định — ASM-nn, mỗi cái có cách xác minh và hệ quả nếu sai.

Kiến trúc là tập hợp những thứ đã loại nhiều hơn là những thứ đã chọn. Không ghi ra thì người sau tưởng bạn đã cân nhắc.

D12 — Sơ đồ và văn bản mâu thuẫn: chia thẩm quyền rõ ràng

Ghi câu này trong mọi tài liệu có sơ đồ:

Sơ đồ thắng về quan hệ và luồng (ai gọi ai, theo thứ tự nào). Bảng/văn bản thắng về ràng buộc và con số (timeout, quyền, định dạng, giới hạn). Mâu thuẫn ngoài hai loại trên ⇒ là lỗi tài liệu, phải sửa chứ không phải chọn bên.


Checklist tự chấm trước khi nộp bất kỳ tài liệu kiến trúc nào

[ ] D1  Mỗi ADR chỉ chứa một quyết định
[ ] D2  grep NFR định tính trả về rỗng; mọi QAS đủ 4 câu (kịch bản/số/cách đo/ai đo)
[ ] D3  Mọi quyết định nêu ≥1 phương án đã loại + lý do gắn với CON hoặc QAS
[ ] D4  Mỗi sơ đồ khai báo mức C4 + có legend, không trộn mức
[ ] D5  Mọi interface có chủ, contract, versioning policy
[ ] D6  Mọi phụ thuộc ngoài process có timeout/retry/fallback + trả lời 4 câu hỏi
[ ] D7  Mỗi thực thể dữ liệu có đúng một single source of truth
[ ] D8  Mọi ràng buộc có FIT hoặc nhãn "⚠️ Khuyến nghị"
[ ] D9  Mọi con số hạ tầng có đơn vị, nguồn, và quy ra tiền; INF khớp TCO
[ ] D10 Mọi chỗ chưa rõ là OQ kèm hệ quả phương án ngược, không phải quyết định ngầm
[ ] D11 Có mục "Ngoài phạm vi" và mục "Giả định ASM-nn"
[ ] D12 Có câu quy định thẩm quyền sơ đồ vs văn bản

In checklist này dạng bảng ☐/✅ ở cuối mỗi lần chạy skill. Mục chưa đạt ⇒ nói rõ thiếu gì, không được đánh ✅ cho có.