# ADR-005 — Message backbone cho luồng đặt hàng là SQS FIFO (per-seller group) + EventBridge, không phải Kafka/MSK *Tên file: `adr/ADR-005_message-backbone-sqs-fifo-eventbridge.md`* | | | |---|---| | **Status** | `Proposed` *(🔴 radar ≥8 — theo `decision-radar.md §2`/§6, **không được chuyển `Accepted` khi chưa có `POC-01` PASS**)* | | **Date** | 2026-09-15 | | **Người quyết** | SA + Tech Lead (tích hợp/hạ tầng — `decision-radar.md §5`) | | **Người đề xuất** | SA (qua skill `sa-2-architecture`, hoạt động `adr`) | | **Điểm radar** | **~8/10** — 🔴 bắt buộc POC trước `Accepted` | | **Supersedes** | — | | **Superseded by** | — | | **Liên quan** | `ASR-005` · `QAS-005` · `QAS-009` (A4 xung đột dòng 2) · `ARISK-01` · `POC-01` · `ADR-001` | > ⚠️ **ADR bất biến sau khi `Accepted`.** Muốn đổi quyết định thì viết ADR mới có > `Supersedes: ADR-005` và đổi trạng thái bản cũ thành `Superseded by`. --- ## 1. Bối cảnh Luồng sự kiện `OrderPlaced`/`PaymentConfirmed` phải truyền tới ≥5 consumer (Commission & Payout, Promotion & Loyalty, Review, Notification, Shipping & Fulfillment) sau khi đặt hàng, đồng thời phải giữ đúng thứ tự xử lý trong cùng một seller (liên quan `DRV-06` — tính nhất quán dữ liệu tài chính). `ASR-001`/`ADR-001` đã chọn hướng managed service (SQS/EventBridge) thay vì tự vận hành Kafka/MSK — ADR này hình thức hoá cụ thể lựa chọn message backbone và **là quyết định đạt ngưỡng radar ≥8** vì chưa có bài đo throughput thật trong ngữ cảnh này (`ASM-07`). **Ràng buộc đang chi phối:** | Nguồn | Nội dung | |---|---| | `ASR-005` | SQS FIFO (message group = `seller_id`) + EventBridge fan-out ≥5 consumer | | `QAS-005` | Thông lượng sustained ≥100 msg/s, p99 publish→consume ≤2s, 0 mất message, fan-out lag ≤5s | | `QAS-009` A4 dòng 2 | Xung đột: AWS giới hạn throughput **cứng cho một message group** SQS FIFO — seller volume cao có thể chạm trần group dù tổng hệ thống chưa chạm 100 msg/s | | `CON-05` | Team chưa xác nhận kinh nghiệm vận hành Kafka/MSK production (`CTX §4.4`) | **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 | |---|---|---| | SQS FIFO hỗ trợ message group theo key tuỳ chọn (`seller_id`) | 🟢 Đã kiểm chứng | Tài liệu AWS công khai | | SQS FIFO + EventBridge đạt ≥100 msg/s sustained, p99 ≤2s trong ngữ cảnh payload `OrderPlaced` ~2KB tại `ap-southeast-1` | 🔴 Giả định (`ASM-07`) | `POC-01` — **chưa chạy** | | Một seller volume rất cao không làm nghẽn message group riêng của seller đó tới mức ảnh hưởng SLA | 🔴 Giả định | Chưa có POC riêng cho kịch bản seller lệch tải — ghi nhận ở §7 | ## 2. Phương án đã cân nhắc ### PA-1 — Kafka / Amazon MSK tự quản lý | | | |---|---| | **Mô tả** | Cụm Kafka/MSK làm xương sống bất đồng bộ, theo `SAD.md`/hồ sơ thầu (P1) | | **Ưu** | Throughput cao, kiểm soát partition/consumer group linh hoạt hơn; công nghệ phổ biến cho hệ thống lớn | | **Nhược** | Không có bằng chứng team từng vận hành Kafka/MSK production (`CTX §4.4`, `OQ-010` mở); effort dựng cụm riêng đã ước 70,2 MD (XL, +35% contingency, `WBS-03`); thêm một cụm phải patch/scale thủ công | | **Chi phí đảo ngược** | Cao — bỏ Kafka/MSK sau khi đã có producer/consumer thật cần viết lại toàn bộ tầng tích hợp bất đồng bộ | ### PA-2 — SQS Standard + SNS fan-out (không FIFO) | | | |---|---| | **Mô tả** | Dùng SQS Standard (at-least-once, không đảm bảo thứ tự) + SNS để fan-out cho nhiều consumer | | **Ưu** | Throughput lý thuyết cao hơn FIFO (không giới hạn theo message group), chi phí thấp | | **Nhược** | **Không đảm bảo thứ tự xử lý trong cùng seller** — vi phạm trực tiếp yêu cầu tính nhất quán tài chính của `DRV-06` (VD 2 sự kiện `OrderPlaced`/`PaymentConfirmed` cùng seller có thể xử lý sai thứ tự, gây sai lệch đối soát) | | **Chi phí đảo ngược** | Trung bình — chuyển sang FIFO sau này cần viết lại consumer để xử lý idempotent/thứ tự, nhưng không cần đổi hạ tầng lớn | ### PA-3 — SQS FIFO (per-seller message group) + EventBridge fan-out *(chọn)* | | | |---|---| | **Mô tả** | SQS FIFO với message group key = `seller_id` (đảm bảo thứ tự trong cùng seller), EventBridge rule fan-out tới ≥5 consumer | | **Ưu** | Managed hoàn toàn (không cụm phải patch/scale thủ công); đảm bảo thứ tự đúng phạm vi cần (trong seller, không cần thứ tự toàn cục); khớp năng lực team hiện tại (không cần kinh nghiệm Kafka) | | **Nhược** | Giới hạn throughput cứng theo **một** message group — seller volume rất cao có thể chạm trần riêng của seller đó (`QAS-009` A4); FIFO có overhead cao hơn Standard | | **Chi phí đảo ngược** | Trung bình — nếu cần chuyển sang Kafka/MSK sau này (do `POC-01` fail hoặc traffic vượt xa dự kiến), phải viết lại toàn bộ tầng publish/consume | ### Bảng so sánh | Tiêu chí | PA-1 (Kafka/MSK) | PA-2 (SQS Standard+SNS) | PA-3 (SQS FIFO+EventBridge) | |---|---|---|---| | Đảm bảo thứ tự trong seller (`DRV-06`) | ✅ (tự cấu hình partition key) | ❌ | ✅ | | Rủi ro năng lực team (`CON-05`) | 🔴 Cao — chưa có kinh nghiệm | 🟢 Thấp | 🟢 Thấp | | Chi phí vận hành (cụm tự quản lý) | 🔴 Cao | 🟢 Thấp | 🟢 Thấp | | Rủi ro trần throughput 1 seller lớn | 🟢 Thấp (partition linh hoạt) | 🟢 Thấp | 🟠 Có (per-group limit) | ## 3. Quyết định > **Chọn PA-3 — SQS FIFO (message group = `seller_id`) + EventBridge fan-out ≥5 consumer.** **Vì sao:** Đây là phương án duy nhất vừa đảm bảo thứ tự trong phạm vi cần (theo seller, khớp `DRV-06`) vừa khớp năng lực team hiện tại (`CON-05`, không cần vận hành cụm Kafka/MSK) — nhất quán với hướng đi đã chọn ở `ADR-001`. **Phạm vi áp dụng:** Toàn bộ luồng sự kiện `OrderPlaced`/`PaymentConfirmed` và các sự kiện tương tự phát sinh sau đặt hàng. ### Đ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** — sustained ≥100 msg/s/30 phút, p99 ≤2s, 0 mất message, fan-out lag ≤5s | Tech Lead + 1 DevOps | 🔴 **Chưa chạy** (`ARISK-01`, `OQ-012`) | | Có phương án theo dõi/cảnh báo riêng cho seller volume cao (giảm rủi ro trần message group) | Tech Lead | Chưa thiết kế — xem §5 "Việc phát sinh" | 🔴 **Bắt buộc theo `decision-radar.md §2`/§6** — điểm radar ~8 (≥8) đồng nghĩa ADR này **không được chuyển `Accepted`** cho tới khi `POC-01` chạy xong và PASS. Nếu `POC-01` FAIL: bổ sung Kafka/MSK riêng cho hot-path đặt hàng (kiến trúc lai, theo điều kiện đã nêu ở `OPT §1`), không Supersede toàn bộ `ADR-001`. ## 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 | |---|---|---|---| | PA-1 — Kafka/MSK | Không có bằng chứng team vận hành production; effort dựng nền tảng lớn hơn đáng kể | `CON-05`, `OQ-010` | Nếu `POC-01` FAIL **và** `OQ-010` xác nhận team có kinh nghiệm Kafka/MSK — bổ sung Kafka/MSK riêng cho hot-path (kiến trúc lai), không thay thế toàn bộ | | PA-2 — SQS Standard + SNS | Không đảm bảo thứ tự trong seller — vi phạm `DRV-06` | `DRV-06` | Chỉ xét lại cho các luồng không yêu cầu thứ tự (VD Notification không cần đúng thứ tự tuyệt đối) — không áp dụng cho `OrderPlaced`/`PaymentConfirmed` | ## 5. Hệ quả **Hệ quả tích cực** - Không cần vận hành cụm message broker riêng — khớp năng lực team, giảm effort nền tảng ~70,2 MD so với Kafka/MSK - Đảm bảo đúng thứ tự xử lý trong phạm vi cần (theo seller) **Hệ quả tiêu cực phải sống chung** - Rủi ro trần throughput cho **một** seller có volume rất cao — cần theo dõi/cảnh báo riêng (chưa thiết kế, xem việc phát sinh) - Nếu `POC-01` FAIL, phải bổ sung kiến trúc lai (Kafka/MSK cho riêng hot-path) — tăng độ phức tạp vận hành so với một giải pháp thuần nhất **Cái quyết định này khoá lại** | Muốn đổi về sau thì | Tốn | |---|---| | Chuyển từ SQS FIFO/EventBridge sang Kafka/MSK sau khi đã có producer/consumer thật | Viết lại toàn bộ tầng publish/consume, thiết kế lại partition/consumer group | **Việc phát sinh** | Việc | Chủ | Hạn | Ghi ở đâu | |---|---|---|---| | Chạy `POC-01` theo tiêu chí đã viết trước | Tech Lead + DevOps | Trước khi `Accepted` | `ARISK_e-commerce_v1.0.md §6` | | Thiết kế cơ chế theo dõi/cảnh báo riêng cho seller volume cao (VD CloudWatch alarm theo group lag) | SA (hoạt động `inf`) + Tech Lead | GĐ2 tiếp theo | `INF_e-commerce` | | Thêm fitness function kiểm tra publish luôn có `seller_id` làm message group key | Tech Lead | GĐ3 (`AGD`/`FIT`) | `FIT-08` (ứng viên) | ## 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` | |---|---|---|---| | `POC-01` — throughput/độ trễ/mất message | k6/artillery, AWS `ap-southeast-1` thật | Trước `Accepted`, lặp lại trước mỗi major release | — (ghi vào `ARISK §6`) | | Mọi message publish `OrderPlaced`/`PaymentConfirmed` có `MessageGroupId = seller_id` | Contract test / lint publish code | CI | `FIT-08` (ứng viên) | | Cảnh báo khi một message group riêng lẻ có lag > ngưỡng | CloudWatch Alarm | Production, liên tục | — | ## 7. Điều kiện xét lại | Dấu hiệu | Ngưỡng | Ai theo dõi | |---|---|---| | `POC-01` FAIL | Không đạt tiêu chí PASS đã viết trước | Tech Lead | | Một seller có volume vượt trần message group SQS FIFO trong production | Lag message group > 5 giây kéo dài > 5 phút liên tục | SRE | | Tổng throughput hệ thống tiệm cận giới hạn đã đo ở `POC-01` | > 80% ngưỡng đã đo | SRE | ## 8. Tham chiếu - POC: `ARISK_e-commerce_v1.0.md §6 POC-01` - Bài đo: `FIT-08` (ứng viên GĐ3) - Tài liệu ngoài: `QAS_e-commerce_v1.0.md §A3 QAS-005, §A4 (xung đột dòng 2)` - Thảo luận: `ASR_e-commerce_v1.0.md §B2 ASR-005`, `OPT_e-commerce_v1.0.md §8 ASM-07` ## 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`) | Radar 8 — bắt buộc `POC-01` (throughput SQS/EventBridge) PASS trước khi `Accepted`; chưa chạy | > Đâ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ý.