192 lines
11 KiB
Markdown
192 lines
11 KiB
Markdown
# 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ẽ theo chuẩn Archify** — thống nhất với quy tắc `W13` của bộ BA; toàn bộ chuẩn ở
|
||
`../../ba-lifecycle/references/diagram-rules.md`. Mỗi sơ đồ gồm **spec JSON Archify** (nguồn sự
|
||
thật về topology, `quality_profile: showcase`, validate 0 lỗi) + **mermaid** trong `.md` có marker
|
||
`<!-- archify: <type> · <spec.json> -->` (bản chiếu, khớp spec hai chiều, không màu) + **bảng đi kèm**.
|
||
Một đường chính (`variant: emphasis`), ≤ 12 node chính, nhãn cạnh ghi giao thức và sync/async.
|
||
|
||
| Sơ đồ | Loại Archify | Mermaid | Sinh ở |
|
||
|---|---|---|---|
|
||
| C4 Context / Container / Component | `architecture` | `flowchart` + `subgraph` | `SAD` §3–§5 |
|
||
| Deployment view | `architecture` | `flowchart TB` + `subgraph` theo vùng/AZ | `SAD` §7, `INF` §3 |
|
||
| Luồng chính, có nhánh lỗi | `sequence` | `sequenceDiagram` | `SAD` §6 |
|
||
| Ranh giới tin cậy | `architecture` | `flowchart LR` | `SEC` §1 |
|
||
| Ownership / luồng dữ liệu | `dataflow` | `flowchart LR` | `DAT` §1 |
|
||
| Mô hình dữ liệu khái niệm | *(mermaid-only)* | `erDiagram` | `DAT` §2 |
|
||
| Class diagram theo bounded context | *(mermaid-only)* | `classDiagram` | `DOM` §2 |
|
||
| Phụ thuộc giữa mục roadmap | `workflow` | `flowchart LR` | `TRM` §4 |
|
||
| Phương án ở mức khối | `architecture` | `flowchart LR` | `OPT` §3 |
|
||
|
||
Spec đặt ở `<thư mục artifact>/diagrams/`, HTML đã `deliver` nằm cạnh. Người điều phối chạy
|
||
`node .claude/skills/ba-lifecycle/scripts/diagram-check.mjs --md <artifact>` sau mỗi activity có sơ đồ.
|
||
|
||
🔴 **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:
|
||
|
||
```markdown
|
||
> **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 + legend, không trộn mức; có spec Archify validate pass, marker, diagram-check xanh
|
||
[ ] 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ó**.
|