207 lines
17 KiB
Markdown
207 lines
17 KiB
Markdown
# 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ý.
|