Files
sys-analysis-design/sa-output/e-commerce/02-architecture/adr/ADR-001_kieu-kien-truc-tong-the-modular-monolith-p2.md
Canhchimlac 343ad8bbbc save
2026-09-15 16:02:30 +07:00

207 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-001 — Kiểu kiến trúc tổng thể của sàn e-commerce là Modular Monolith + managed AWS (P2), không phải ~10 Microservices độc lập (P1)
*Tên file: `adr/ADR-001_kieu-kien-truc-tong-the-modular-monolith-p2.md`*
| | |
|---|---|
| **Status** | `Proposed` |
| **Date** | 2026-09-15 |
| **Người quyết** | SA + Tech Lead (theo `decision-radar.md` §5 — "Cấu trúc hệ thống" đề xuất SA, chốt SA+Tech Lead; EA phủ quyết nếu lệch chuẩn doanh nghiệp). **Chưa ký thật** — chỉ có Điều phối dự án ký thay theo ngoại lệ `DEC-01`/`DEC-17`, xem §Điều kiện chuyển Accepted |
| **Người đề xuất** | SA (qua skill `sa-2-architecture`, hoạt động `adr`) |
| **Điểm radar** | **~7/10** *(chấm lại theo `decision-radar.md §2` — xem §Radar dưới; dưới ngưỡng 8 cứng nhưng vẫn cần POC vì `ASM-07`/`ASM-08` chưa xác minh)* |
| **Supersedes** | — |
| **Superseded by** | — |
| **Liên quan** | `ASR-001` · `QAS-005` · `QAS-006` · `QAS-009` · `CON-03` · `CON-05` · `CON-06` · `DRV-04` · `DEC-06` · `POC-01` · `POC-02` · `OQ-010` · `OQ-012` |
> ⚠️ **ADR bất biến sau khi `Accepted`.** Muốn đổi quyết định thì viết ADR mới có
> `Supersedes: ADR-001` và đổi trạng thái bản cũ thành `Superseded by`. Không sửa nội dung
> ADR đã Accepted, không xoá ADR đã Rejected.
---
## 1. Bối cảnh
Sàn e-commerce là dự án greenfield (`CTX §4.1`), quy mô mục tiêu "lớn" (`DRV-04`: hàng trăm
nghìn SKU, hàng trăm nghìn–hàng triệu user, đỉnh hàng nghìn–chục nghìn concurrent user mùa flash
sale — kịch bản A 2.000 CCU/~400 đơn/giờ, kịch bản B 10.000 CCU/~15.000 đơn/giờ, `QAS-009`).
`OPT_e-commerce_v1.0.md` (Bước 4 GĐ1) đã dựng và chấm điểm 3 phương án khả thi (P0/P1/P2) cộng
2 phương án bị loại ở gate cứng (P3/P4) — kết luận khuyến nghị **P2** (modular monolith 2–3 nhóm
ECS Fargate + managed AWS, Payment tách riêng), duyệt từng phần bởi PO uỷ quyền tại `DEC-06`,
nhưng `DEC-06` tự ghi rõ **chưa thay thế** yêu cầu `ADR` chính thức (kiểu kiến trúc tổng thể là
quyết định thuộc danh mục bắt buộc `ADR` theo `decision-radar.md §3`). `ASR-001` (chưng cất từ
`DEC-06`) xác nhận đây là ràng buộc cấu trúc bao trùm toàn bộ hệ thống, ép ra ranh giới container
ở `SAD §4`. ADR này **trả nợ** `DEC-06`/`DTM §11 "🟠 Nợ #1"`/`ADL §8` — không phải quyết định
mới, mà là hình thức hoá quyết định đã có hướng đi tạm.
**Ràng buộc đang chi phối:**
| Nguồn | Nội dung |
|---|---|
| `CON-03` | Cloud AWS bắt buộc (cứng) — loại mọi phương án ngoài AWS ngay từ `OPT` |
| `CON-05` | Số đội/số người thi công **chưa xác nhận thật** — giả định tạm "team MVP chuẩn" (`DEC-02`), tham khảo hồ sơ thầu: BE trung bình ~2,6 FTE/tháng trên 7 tháng, đỉnh 10 người toàn dự án |
| `CON-06` | PCI-DSS SAQ A + NĐ13/2023 — Payment phải là biên cô lập bất kể chọn P1 hay P2 (áp dụng độc lập với ADR này, xem `ADR-002`) |
| `ASR-001` | Ép ranh giới triển khai 2–3 nhóm ECS Fargate + 1 nhóm Payment riêng; ép dùng SQS/EventBridge thay Kafka/MSK; ép 1 RDS chính schema-per-module |
| `QAS-005`/`QAS-006`/`QAS-009` | Thông lượng/độ trễ/khả năng mở rộng phải đạt ở kịch bản A/B — input trực tiếp cho `POC-01`/`POC-02` |
**Cái đã biết chắc / cái còn là giả định:**
| Điều | 🟢 Đã kiểm chứng / 🔴 Giả định | Bằng chứng |
|---|---|---|
| AWS là cloud bắt buộc | 🟢 Đã kiểm chứng | `CON-03`, xác nhận vòng 3 Q&A brief |
| Payment phải cô lập PCI-DSS SAQ A | 🟢 Đã kiểm chứng | `CON-06`, pháp luật/hợp đồng đối tác |
| SQS FIFO + EventBridge đủ throughput cho `OrderPlaced`/`PaymentConfirmed` ở tải đỉnh | 🔴 Giả định (`ASM-07`) | `POC-01` — **chưa chạy** |
| 1 cụm RDS chính đủ tải ghi/đọc gộp | 🔴 Giả định (`ASM-08`) | `POC-02` — **chưa chạy** |
| Team thi công thật khớp giả định "team MVP chuẩn" (`DEC-02`) | 🔴 Giả định | `OQ-010` — Tech Lead chưa xác nhận |
| P2 rẻ hơn P1 ở TCO 3 năm (sơ bộ, số lượng thành phần) | 🟡 Ước lượng có cơ sở | `TCO §3.2` (chưa quy đổi VND đầy đủ, đơn giá AWS chưa xác nhận `OQ-013`) |
## 2. Phương án đã cân nhắc
*Tham chiếu đầy đủ bảng chấm điểm và sơ đồ mức khối đã có ở `OPT_e-commerce_v1.0.md §3, §4, §5`
— **không lặp lại** ở đây theo đúng ghi chú người duyệt. Tóm tắt lại đúng mức cần để ra quyết
định.*
### PA-1 — P1: Modular microservices (~10 service, database-per-service, Kafka/MSK, OpenSearch)
| | |
|---|---|
| **Mô tả** | ~10 service theo bounded-context, mỗi service 1 RDS logic riêng, Kafka/MSK làm xương sống bất đồng bộ, OpenSearch đa node cho search, ECS/EKS auto-scaling theo domain — theo `SAD.md` v0.2/hồ sơ thầu (`OPT §3` P1) |
| **Ưu** | Đáp ứng `DRV` Must cao nhất (`OPT §4`: điểm 5/5) — scale-out **độc lập** Catalog/Search khỏi Cart/Checkout ngay từ ngày một (`DRV-04`); ranh giới service rõ, dễ thay/rút từng service riêng lẻ về sau |
| **Nhược** | Vi phạm Conway rõ với team giả định hiện tại — mỗi BE ôm trung bình 3–4 service (`CON-05`, `OPT §4` tiêu chí 5: P1=2/5); không có bằng chứng team từng vận hành Kafka/MSK/OpenSearch/EKS production (`CTX §4.4`, `OQ-010` mở); effort dựng nền tảng riêng đã ước 70,2 MD (`WBS-03`, XL, +35% contingency) trước khi làm nghiệp vụ; nhiều thành phần managed độc lập phải vận hành hơn |
| **Chi phí đảo ngược** | Gộp ngược lại thành ít service hơn (nếu team quá nhỏ để vận hành 10 service) tốn công tái cấu trúc network/DB đáng kể — ước > 4 tuần |
### PA-2 — P2: Modular monolith (2–3 nhóm ECS Fargate) + managed AWS, Payment tách riêng *(chọn)*
| | |
|---|---|
| **Mô tả** | Modular monolith theo domain, chạy 2–3 nhóm ECS Fargate (Nhóm Giao dịch / Nhóm Hỗ trợ), SQS FIFO + EventBridge thay Kafka/MSK, 1 RDS chính schema-per-module, Postgres FTS giai đoạn đầu (hoãn OpenSearch); Payment tách hoàn toàn (network/IAM/DB riêng) — theo `OPT §3` P2 |
| **Ưu** | Khớp Conway với team giả định (`OPT §4` tiêu chí 5: P2=5/5); effort nền tảng thấp hơn P1 (-70,2 MD, không cần dựng cụm Kafka/MSK); ít thành phần managed độc lập phải vận hành hơn (chi phí vận hành sơ bộ thấp hơn); rủi ro công nghệ thấp hơn (managed service phổ biến hơn) |
| **Nhược** | Thua P1 đúng 1 bậc ở tiêu chí "Đáp ứng `DRV` Must" (4/5 so với 5/5, `OPT §4`) — scale-out **độc lập** Catalog/Search khỏi Cart/Checkout **kém hơn** ở giai đoạn đầu (chung nhóm "Nhóm Giao dịch"); nếu traffic thật vượt xa dự kiến, tách Catalog/Search ra khỏi monolith muộn sẽ tốn hơn tách từ đầu |
| **Chi phí đảo ngược** | Module hoá rõ theo domain trong code giúp tách thành service riêng về sau **có kế hoạch** khi có bằng chứng tải thật — nhưng tốn công tách hạ tầng (network, DB, CI/CD riêng) tại thời điểm tách, không miễn phí; ước > 4 tuần nếu tách toàn diện |
### PA-0 — Không xây gì (P0, mốc so sánh)
Vi phạm trực tiếp `DRV-01` (Must, blocker MVP) — không phải phương án khả thi, chỉ giữ làm mốc
so sánh (`OPT §4`). Không xét tiếp.
### Bảng so sánh *(tóm tắt — chi tiết đầy đủ 6 tiêu chí × trọng số ở `OPT §4`)*
| Tiêu chí *(trọng số)* | P1 | P2 |
|---|---|---|
| Đáp ứng `DRV` Must (25%) | 5 | 4 |
| Chi phí 3 năm sơ bộ (20%) | 2 | 4 |
| Thời gian tới bản chạy được (15%) | 2 | 4 |
| Rủi ro kỹ thuật (15%) | 2 | 4 |
| Năng lực team & vận hành — Conway (15%) | 2 | 5 |
| Khả năng tiến hoá (10%) | 4 | 3 |
| **Tổng có trọng số** | **2.95** | **4.05** |
Chênh lệch 1.10 điểm — đủ phân biệt theo `OPT §4` (không rơi vào bẫy "chênh <0.3").
## 3. Quyết định
> **Chọn PA-2 (P2) — Modular monolith + managed AWS, Payment tách riêng.**
Toàn sàn e-commerce được tổ chức thành 2–3 nhóm triển khai ECS Fargate (modular monolith theo
domain — Nhóm Giao dịch: Identity & Access, Catalog & Inventory, Cart & Order; Nhóm Hỗ trợ:
Seller Management, Commission & Payout, Promotion & Loyalty, Review, Notification, Shipping &
Fulfillment), cộng 1 Payment Service tách hoàn toàn về mạng/IAM/dữ liệu. Dùng SQS FIFO +
EventBridge (không phải Kafka/MSK) và 1 cụm RDS PostgreSQL chính schema-per-module (không phải
database-per-service).
**Vì sao:** Khớp `CON-05` (Conway — năng lực team giả định hiện tại), giữ nguyên `CON-06` (Payment
cô lập, độc lập với lựa chọn này), và đạt điểm tổng cao nhất trong bộ tiêu chí đã chốt ở `OPT §2`
(4.05 so với 2.95 của P1).
**Phạm vi áp dụng:** Toàn bộ sàn e-commerce, đến khi traffic thật tiệm cận kịch bản B × 2
(~20.000 concurrent) mà chưa kiểm chứng lại bằng POC/bài đo thật (xem `QAS-009` "Ngoài phạm vi").
### Điều kiện chuyển `Proposed → Accepted`
| Điều kiện | Ai xác nhận | Trạng thái hiện tại |
|---|---|---|
| `POC-01` PASS (throughput SQS FIFO/EventBridge ≥100 msg/s sustained, p99 ≤2s, 0 mất message) | Tech Lead + 1 DevOps | **Chưa chạy** (`ARISK-01`, `OQ-012`) |
| `POC-02` PASS (RDS gộp: p95 write ≤300ms, p95 read ≤200ms, CPU <75%) | Tech Lead/DBA | **Chưa chạy** (`ARISK-02`) |
| `OQ-010` — Tech Lead xác nhận team thi công thật khớp (hoặc gần khớp) giả định "team MVP chuẩn" | Tech Lead | Mở |
| AG1 ký thật (PO + Tech Lead, không phải ký thay) | PO + Tech Lead | Mở (`OQ-004`) |
🔴 Dù radar ước lượng ~7 (< 8 ngưỡng cứng), ADR này **vẫn không được chuyển `Accepted`** cho tới
khi cả hai `POC` chạy xong — vì bản thân `ASM-07`/`ASM-08` (nền tảng của toàn bộ lựa chọn P2)
chưa xác minh; đây là cam kết đã ghi tại `OPT §1` "Điều kiện kèm theo" và `DEC-06`.
## 4. Phương án bị loại và lý do
| Phương án | Loại vì | Gắn với | Điều kiện nào thì xét lại |
|---|---|---|---|
| P0 — Không xây gì | Vi phạm trực tiếp `DRV-01` (Must, blocker MVP) | `DRV-01` | Không xét lại — không phải phương án khả thi cho một sàn giao dịch |
| P1 — Modular microservices (~10 service) | Vi phạm Conway với team giả định hiện tại (`CON-05`, mỗi BE ôm 3–4 service); không có bằng chứng team từng vận hành Kafka/MSK/OpenSearch/EKS production (`CTX §4.4`) | `CON-05`, `OPT §4` tiêu chí 4/5 | Nếu `OQ-010` xác nhận team thật có kinh nghiệm Kafka/MSK/OpenSearch/EKS production **và** team đủ lớn (gần đỉnh 10 người đề xuất hồ sơ thầu) — chấm lại theo `OPT §9 OQ-010` |
| P3 — Mua/tích hợp nền tảng mã nguồn mở | Loại ở gate cứng `OPT §2.1` — không kiểm chứng được cô lập Payment cho PCI-DSS SAQ A (`CON-06`) mà không sửa sâu lõi | `CON-06` | Nếu Tech Lead xác nhận kinh nghiệm sâu với một nền tảng cụ thể **và** POC chứng minh cô lập Payment đạt SAQ A — xét lại cho module không nhạy cảm tài chính (`OPT §5`) |
| P4 — Serverless-first (Lambda/Step Functions/DynamoDB) | Cold-start Lambda ảnh hưởng `DRV-02` (bỏ giỏ) chưa có POC đo trong ngữ cảnh VPC+RDS; chưa có bằng chứng năng lực team serverless-at-scale (`CON-05`) | `DRV-02`, `CON-05` | Nếu có POC đo cold-start đạt yêu cầu và Tech Lead xác nhận năng lực — xét lại cho luồng bất đồng bộ thuần (Notification, Commission batch) như lựa chọn lai, không toàn hệ thống (`OPT §5`) |
## 5. Hệ quả
**Hệ quả tích cực**
- Số đơn vị triển khai cần vận hành/on-call giảm từ ~10 (P1) xuống ~3 (2–3 nhóm monolith +
Payment) — khớp trực tiếp FTE DevOps trung bình thấp (~0,7 FTE, `TCO`/hồ sơ thầu)
- Effort tới bản chạy được thấp hơn P1 khoảng 70,2 MD (không cần dựng cụm Kafka/MSK)
- Rủi ro công nghệ thấp hơn — managed service phổ biến hơn (RDS, SQS/EventBridge, ECS Fargate)
**Hệ quả tiêu cực phải sống chung**
- Scale-out **độc lập** Catalog/Search khỏi Cart/Checkout kém hơn P1 ngay từ ngày một — nếu flash
sale gây tải lệch mạnh giữa Catalog và Cart&Order, cả hai vẫn scale chung trong "Nhóm Giao dịch"
cho tới khi tách (xem `ASR-014`/`ADR-012`)
- Nếu `POC-01`/`POC-02` FAIL: phải bổ sung kiến trúc lai (Kafka/MSK riêng cho hot-path đặt hàng,
hoặc tách read replica sớm hơn dự kiến) — không quay lại P1 hoàn toàn (`OPT §1`)
**Cái quyết định này khoá lại**
| Muốn đổi về sau thì | Tốn |
|---|---|
| Tách toàn bộ modular monolith thành microservices sau go-live | Tái cấu trúc network/DB/CI-CD riêng biệt cho từng module — ước > 4 tuần, có rủi ro downtime khi tách |
| Đổi lại SQS/EventBridge sang Kafka/MSK sau khi đã có producer/consumer thật | Viết lại toàn bộ tầng tích hợp bất đồng bộ — xem `ADR-005` (quyết định riêng, không lặp ở đây) |
**Việc phát sinh**
| Việc | Chủ | Hạn | Ghi ở đâu |
|---|---|---|---|
| Chạy `POC-01`/`POC-02` theo tiêu chí PASS/FAIL đã viết trước | Tech Lead + DevOps/DBA | Trước khi ADR này chuyển `Accepted` | `ARISK_e-commerce_v1.0.md §6` |
| Xác nhận `OQ-010` (team thật) | Tech Lead | Trước khi `Accepted` | `00-index/OQ_e-commerce.md` |
| Thêm fitness function kiểm tra ranh giới module (không cho import chéo giữa domain package) | SA + Tech Lead | GĐ3 (`AGD`/`FIT`) | `FIT-01` (ứng viên) |
| Kế hoạch tách Catalog/Search nếu traffic vượt ngưỡng B×2 | Tech Lead | Khi có bằng chứng traffic thật | `TDEBT` (GĐ3) |
## 6. Cách kiểm chứng quyết định này được tuân thủ
| Cách kiểm | Công cụ | Chạy ở đâu | `FIT` |
|---|---|---|---|
| Không có import chéo trực tiếp DB giữa các domain package trong monolith (chỉ qua API nội bộ/event) | dependency-cruiser hoặc ArchUnit-style rule | CI | `FIT-01` (ứng viên, GĐ3) |
| Số đơn vị triển khai (ECS service) đúng 2–3 nhóm + 1 Payment, không phình thêm không kiểm soát | Kiểm tra hạ tầng (Terraform plan review) | CI/CD pipeline | `FIT-02` (ứng viên) |
| `POC-01`/`POC-02` — bài đo throughput/tải | k6/artillery, load test staging | Trước go-live, lặp lại trước mỗi major release | — (kết quả ghi vào `ARISK §6`) |
## 7. Điều kiện xét lại
| Dấu hiệu | Ngưỡng | Ai theo dõi |
|---|---|---|
| Traffic thật tiệm cận kịch bản B × 2 | ~20.000 concurrent user | Tech Lead/SRE |
| `POC-01` hoặc `POC-02` FAIL | Không đạt tiêu chí PASS đã viết trước (`ARISK §6`) | Tech Lead |
| Team thật khác biệt đáng kể so với giả định "team MVP chuẩn" | Số BE thật < 50% giả định `DEC-02`, hoặc > gấp đôi | Tech Lead/PM |
## 8. Tham chiếu
- POC: `ARISK_e-commerce_v1.0.md §6 POC-01, POC-02`
- Bài đo: chưa chạy — xem §Điều kiện chuyển Accepted
- Tài liệu ngoài: `OPT_e-commerce_v1.0.md` §1, §3, §4, §5 (bảng chấm điểm đầy đủ, không lặp lại) ·
`SAD_e-commerce_v1.0.md §2, §4` (hiện thực hoá quyết định này thành C4 Container)
- Thảo luận: `DEC-06` (00-index/DEC_e-commerce.md), duyệt từng phần 2026-09-12
## 9. Review log (không đổi Status)
| Ngày | Người review | Vai trò | Quyết định | Lý do chưa chuyển `Accepted` |
|---|---|---|---|---|
| 2026-09-15 | Điều phối dự án (thay mặt Tech Lead, chế độ chạy thử) | Tech Lead (ký thay, ngoại lệ `DEC-01`) | `Reviewed (Proposed giữ nguyên)` — nội dung đủ làm cơ sở thiết kế tiếp (`icd`/`dat`/`sec`/`inf`/`fail`) | Chưa có chữ ký thật Tech Lead; `POC-01`+`POC-02` chưa chạy; `OQ-010` (team thật) chưa xác nhận |
> Đây là ghi nhận **review nội dung**, không phải "sign" theo nghĩa gate: `Status` giữ nguyên
> `Proposed`. Xem `00-index/ADL_e-commerce.md` (Change Log — Review 2026-09-15) và `ADL §8`
> "Việc phải làm" cho điều kiện chuyển `Accepted`. Confidence tổng thể của lượt review: 🔴 —
> `AG2` chưa ký.