This commit is contained in:
Canhchimlac
2026-09-15 16:02:30 +07:00
parent d169c85dd4
commit 343ad8bbbc
37 changed files with 9726 additions and 61 deletions

View File

@@ -0,0 +1,600 @@
# ICD — Integration & Interface Catalog — e-commerce
| | |
|---|---|
| **Version** | 1.0 |
| **Date** | 2026-09-15 |
| **Author** | SA (skill sa-2-architecture, chế độ `go`, hoạt động `icd`) |
| **Status** | 🟡 Draft *(duyệt từng phần — xem `Approved by`; AG2 cần cả ba vai trò (Tech Lead/Security/Ops-SRE) ký, mới có một chữ ký thay có điều kiện — **chưa đủ điều kiện chuyển `🔵 Approved`**)* |
| **Approved by** | Tech Lead: **Điều phối dự án** (uỷ quyền, chế độ chạy thử — ký thay theo ngoại lệ `DEC-01`, **không phải chữ ký Tech Lead thật**) — duyệt **từng phần** bản v1.0 · 2026-09-15: chấp nhận catalog 17 `IF-nn`, chi tiết đầy đủ `IF-002` (4 endpoint Cart & Checkout), và kết luận xác nhận/điều chỉnh `API_US002-003_v1.0.md` của BA tại §5 (`GET /v1/cart` nhóm theo seller; `sellerName` denormalize snapshot; thêm `unitPriceSnapshotVnd`/`currentPriceVnd`/`priceChanged`; Guest ownership `403` dùng cùng cơ chế theo `ADR-007`). Chấp nhận **TẠM** `ASM-24` (`IF-021` cross-network), `ASM-25` (chiều `IF-022` Cart→Seller), `ASM-26` (Payment Service nhận `sellerId` ngay tại `IF-006`), và hướng denormalize `sellerName` của `OQ-033` làm baseline cho hoạt động `dat`/`sec`/`inf`/`fail` kế tiếp — **`OQ-028…033` (SA) giữ nguyên MỞ**, chờ Tech Lead thật xác nhận, không coi là đã đóng. Ghi nhận lại: 5 interface đối tác ngoài (VNPay/Momo/GHN/GHTK) và Email/SMS Provider **chưa có contract thật** (🔴, `ARISK-03/04`) — không đổi bởi lượt duyệt này. Ký thay Tech Lead theo ngoại lệ `DEC-01` (SA) — **không phải chữ ký Tech Lead thật**. Nhắc BA: chạy `ba-pipeline` hoạt động `specification` để cập nhật `API_US002-003`/`SRS_US002-003` theo `ICD §5`. · Security: — *(chưa ký)* · Ops/SRE: — *(chưa ký)* — AG2 cần cả ba cùng ký; tài liệu này **vẫn chưa qua AG2**, còn xa vì `DAT`/`SEC`/`INF`/`FAIL` (hoạt động 5–8) chưa chạy và không `ADR` nào `Accepted`. `OQ-004` (tính hợp lệ ký thay) vẫn mở. `Confidence` **giữ nguyên** 🔴 — duyệt từng phần không phải bằng chứng nguồn mới, không nâng `Confidence`. Status **giữ** 🟡 Draft — AG2 chưa đủ ba chữ ký thật/hợp lệ. Ghi thêm `DEC-19`. |
| **Source** | `02-architecture/SAD_e-commerce_v1.1.md` §3, §4, §4.1, §4.3, §6, §6.3, §7 · `02-architecture/ASR_e-commerce_v1.0.md` `ASR-002/003/004/005/007/008/013` · `02-architecture/QAS_e-commerce_v1.0.md` `QAS-003/004/005/010/011` · `02-architecture/adr/ADR-003_*.md`, `ADR-004_*.md`, `ADR-005_*.md`, `ADR-007_*.md`, `ADR-011_*.md` · `00-index/OQ_e-commerce.md` v1.13 · `00-index/DEC_e-commerce.md` v1.15 · `ba-output/e-commerce/03-specification/API_US002-003_v1.0.md` · `ba-output/e-commerce/03-specification/SRS_US002-003_v1.0.md` · `ba-output/e-commerce/03-specification/AC_US-002_v1.0.md`, `AC_US-003_v1.0.md` · `ba-output/e-commerce/02-analysis/RBAC_CartCheckout_v1.0.md` · `ba-output/e-commerce/00-index/OQ_e-commerce.md` (`OQ-029/030/032/033/034`) · `e-commerce/docs/sections/04-api-design.md` (§4.1.1, §4.1.5, §4.1.6, §4.1.13, §4.2, §4.3 — quy ước chung tái sử dụng, **không** tái sử dụng kiểu kiến trúc ~10 microservices/Kafka của tài liệu này) |
| **Scope** | 22 interface ứng viên `IF-nn` của `SAD §4` — toàn sàn theo P2 (chỉ chốt ở mức catalog: hai đầu/giao thức/owner/versioning/đầu kia hỏng thì sao). Chi tiết contract **đầy đủ** cho **Cart & Order** (US-002/US-003: `GET`/`PATCH`/`DELETE /v1/cart*`, `POST /v1/checkout`) để xác nhận `API_US002-003_v1.0.md` của BA. 🔴 Xem §0.1 — chỉ **17/22** `IF-nn` có ID riêng biệt truy được trong nội dung `SAD`; chênh lệch được ghi nhận, không tự bịa 5 ID còn thiếu. |
| **Confidence** | 🔴 Giả định chưa xác minh — theo yêu cầu điều phối, tiếp nối `Confidence` 🔴 của `SAD`/`ASR`/`QAS`. AG1 chưa ký thật; AG2 chưa tới hạn; `ADR-001…012` đều `Proposed` (chưa `Accepted`); `POC-01`/`POC-02` chưa chạy; không đối tác nào (VNPay/Momo/GHN/GHTK/Email-SMS) có sandbox/tài liệu thật (`ARISK-03/04`, `CTX §4.2`). |
## Change Log
| Version | Date | Người sửa | Thay đổi | ADR/DEC |
|---|---|---|---|---|
| 1.0 | 2026-09-15 | SA (qua skill sa-2-architecture, chế độ `go`, hoạt động `icd`) | Bản đầu — hoạt động 4/9 của GĐ2. Chốt catalog cho 17 `IF-nn` truy được từ `SAD §4`/§6.3 (chênh lệch với con số "22" của `SAD` được ghi nhận, không bịa thêm — xem §0.1, `OQ-029`). Chi tiết đầy đủ (mục đích, quyền, idempotent, tần suất, mã lỗi, versioning) cho `IF-002` (client ↔ Nhóm Giao dịch — 4 endpoint Cart & Checkout), `IF-005`, `IF-006`, `IF-007/008` (VNPay/Momo), `IF-009` (event `OrderPlaced`/`PaymentConfirmed`, ordering key `seller_id` theo `ADR-005`), `IF-015`, `IF-021`, `IF-022`. Xác nhận/điều chỉnh từng endpoint của `API_US002-003_v1.0.md` (BA) tại §5 — 5 mục xác nhận, 1 mục điều chỉnh (denormalize `sellerName`), 1 mục chờ `OQ-030(BA)`; đóng đề xuất `OQ-034(BA)`/`OQ-032(BA)` (BA cần cập nhật SRS/API để đóng chính thức trong sổ BA). Thêm `OQ-028…033` (SA sổ), `ASM-24…26`. Ghi `DEC-18` (tiếp nối `DEC-01…17`, ngoại lệ gate tiếp tục GĐ2 hoạt động `icd`). Không sửa `SAD`/`ASR`/`QAS`/`ADR` đã có. `Confidence` 🔴 toàn bộ. | `DEC-18` |
| 1.0 | 2026-09-15 | Điều phối dự án (thay mặt Tech Lead, ngoại lệ `DEC-01`) — tác vụ **sign/approve** | Duyệt **từng phần**: chấp nhận catalog 17 `IF-nn`, chi tiết `IF-002` (4 endpoint Cart & Checkout), kết luận xác nhận/điều chỉnh `API_US002-003` của BA (nhóm theo seller `GET /v1/cart`; `sellerName` denormalize snapshot; thêm `unitPriceSnapshotVnd`/`currentPriceVnd`/`priceChanged`; Guest ownership `403` theo `ADR-007`). Chấp nhận **TẠM** `ASM-24` (`IF-021` cross-network), `ASM-25` (chiều `IF-022` Cart→Seller), `ASM-26` (Payment nhận `sellerId` ở `IF-006`), và hướng denormalize `sellerName` của `OQ-033` làm baseline cho `dat`/`sec`/`inf`/`fail` kế tiếp — **`OQ-028…033` (SA) giữ nguyên MỞ**, chờ Tech Lead thật xác nhận. Ghi nhận lại: interface đối tác ngoài (VNPay/Momo/GHN/GHTK) và Email/SMS Provider **chưa có contract thật** (🔴, `ARISK-03/04`). Không đổi nội dung chuyên môn. Ký thay Tech Lead theo ngoại lệ `DEC-01`; Security/Ops chưa ký; `OQ-004` mở; `Confidence` giữ 🔴; Status giữ 🟡 Draft; AG2 **chưa** ký. Nhắc BA chạy `ba-pipeline` hoạt động `specification` để cập nhật `API_US002-003`/`SRS_US002-003` theo `ICD §5`. Ghi thêm `DEC-19`. | `DEC-19` |
> 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 chọn bên (`D12`).
> 🔴 **Tài liệu này thắng `API` contract của bộ BA khi hai bên lệch** (`artifact-map.md §7`). BA
> viết contract đề xuất và đánh dấu *"chờ BE xác nhận"* — đây là chỗ xác nhận. Mọi chỗ lệch được
> liệt kê ở §5 kèm hành động cho BA.
---
## 0. Ghi chú Preflight & phạm vi — bắt buộc đọc trước
### 0.0 Ngoại lệ gate
**AG1 vẫn chưa ký thật; AG2 (gate của chính GĐ2) chưa tới hạn.** Theo `SKILL.md`, hoạt động 1
(`QAS`) → 2 (`ASR`) → 3 (`SAD`) đã hoàn tất và được duyệt từng phần (`DEC-12/14/16`) — điều kiện
đầu vào cho hoạt động 4 (`ICD`) đã đủ. Người dùng (vai điều phối dự án chạy thử) đã được cảnh báo
lại và **khẳng định muốn tiếp tục**.
> **DEC-18 (SA)** — Tiếp tục `sa-2-architecture` (hoạt động 4 — `ICD`) cho e-commerce trong khi
> AG1 chưa ký thật và AG2 chưa tới hạn — tiếp nối `DEC-01…17`.
> **Quyết định:** Chốt catalog + chi tiết contract cho các `IF-nn` liên quan Cart & Order dựa
> trên `SAD v1.1 §4` đã duyệt từng phần, giữ `Confidence 🔴` cho tới khi: (a) AG1/AG2 ký thật,
> (b) đối tác VNPay/Momo/GHN/GHTK có sandbox/tài liệu thật (`ARISK-03/04`), (c) Tech Lead xác
> nhận các đề xuất mới của ICD (schema sự kiện, denormalize `sellerName`, hướng `IF-021`/`IF-022`).
> **Người quyết:** Điều phối dự án (đại diện PO, dự án chạy thử).
> **Radar (ước lượng):** ~4 — chi phí đảo ngược thấp (contract catalog sửa lại khi có Tech Lead
> thật không tốn nhiều, chưa có dòng code nào phụ thuộc) · bán kính ảnh hưởng: hoạt động `dat`/
> `sec`/`inf`/`fail` kế tiếp và toàn bộ dev BE/FE dùng `ICD` làm nguồn sự thật, nhưng bản thân
> *việc ghi tài liệu dưới ngoại lệ* thì nhỏ · có chạm trực tiếp việc xác nhận `API` của BA (đúng
> vai trò `ICD` phải làm) nhưng chưa phải cam kết thi công (chưa ai ký) · không ràng buộc dài hạn
> tự thân (ICD sửa được qua version mới) · không tranh cãi mới, tiếp nối tiền lệ `DEC-11…17` →
> **ghi `DEC-nn`, không cần `ADR` cho việc chạy dưới ngoại lệ**.
> **Hệ quả nếu không chấp nhận ngoại lệ:** BA không có ai xác nhận chính thức 3 endpoint Cart
> đang ở trạng thái "chờ BE" — SRS/API của BA tiếp tục treo `OQ-030/032/033/034(BA)`, chặn G3 của
> bộ BA (cần SA qua AG2, nhưng cần `ICD` xác nhận `API` trước khi G3 có ý nghĩa).
### 0.1 Chênh lệch số lượng `IF-nn`: SAD nói 22, ICD chỉ truy được 17
`SAD_e-commerce_v1.1.md` (header Scope, Change Log v1.0, §Tự chấm) nhắc nhiều lần "22 `IF-nn`
ứng viên", nhưng khi rà toàn bộ §3, §4.1 (bảng `CMP-nn`), §5, §6.3 chỉ có **17 ID riêng biệt**
thực sự xuất hiện: `IF-001, 002, 003, 004, 005, 006, 007, 008, 009, 015, 016, 017, 018, 019, 020,
021, 022`. Không có `IF-010…014` ở bất kỳ đâu trong văn bản.
🔴 **ICD không tự bịa 5 ID còn thiếu** để khớp con số "22" (vi phạm nguyên tắc "không bịa"/D1).
Hai khả năng hợp lý: (a) tác giả `SAD` đã đếm nhầm khi tổng hợp số liệu ở header/changelog, hoặc
(b) có ý định tách nhỏ hơn (VD từng endpoint `GET`/`PATCH`/`DELETE /v1/cart*` thành các `IF` riêng
thay vì gộp chung `IF-002`) nhưng chưa thực hiện trong bảng `CMP-nn`. Ghi nhận ở `OQ-029` (SA sổ)
— đề nghị rà lại `SAD` ở lần cập nhật kế tiếp, **không chặn** hoạt động `icd` này vì 17 ID hiện có
đã đủ bao phủ mọi cặp container/hệ ngoài xuất hiện trong `SAD §3/§4/§6.3`.
**Cách ICD xử lý granularity:** ở mức catalog (§2), giữ đúng 17 ID theo `SAD` (mức container-to-
container, đúng độ chi tiết `SAD` đã chọn). Ở mức chi tiết (§3), với `IF-002` (client ↔ Nhóm Giao
dịch) — vốn gộp chung Identity/Catalog/Cart&Order ở mức `SAD` — ICD **tách nhỏ theo endpoint**
ngay bên trong mục `IF-002` cho phần Cart & Order (4 endpoint), vì đây là yêu cầu "chi tiết đầy đủ
để xác nhận `API` của BA" — không tạo `IF-nnn` mới cho việc này, chỉ chia nhỏ nội dung trong cùng
một dòng catalog (nhất quán với `D5`: "mọi lời gọi vượt ranh giới container phải có ≥1 dòng
`IF-nnn`" — 4 endpoint này cùng vượt đúng một ranh giới container Client→Nhóm Giao dịch).
---
## 1. Quy ước chốt một lần cho toàn hệ thống
*Kế thừa và xác nhận lại (không đổi) từ `e-commerce/docs/sections/04-api-design.md` §4.1.1,
§4.1.13, §4.3 (status: approved trong tài liệu gốc, nhưng tài liệu đó thuộc kiến trúc P1 đã loại
— ICD xác nhận các quy ước này **độc lập với kiểu kiến trúc**, vẫn áp dụng đúng cho P2). Radar
ước lượng ~3 (đã có tiền lệ, không tranh cãi, chi phí đảo ngược thấp) ⇒ theo
`decision-radar.md §2`, **không cần `ADR` riêng** — ghi nhận tại đây là đủ.*
| # | Vấn đề | Quyết định | Vì sao |
|---|---|---|---|
| 1 | **Số lớn** (id, số tiền) truyền dạng gì | `string` cho mọi id (`cartId`, `cartItemId`, `orderId`, `sellerId`, `productVariantId`...); số tiền `*Vnd` dạng `number` (VND là số nguyên, không có phần thập phân, không vượt `2^53` ở quy mô GMV hiện tại — xác nhận lại nếu tổng giỏ hàng B2B vượt hàng chục tỷ) | Khớp `API_US002-003 §1`, `04-api-design.md` (ví dụ `"orderId": "order_001"`) |
| 2 | **Thời gian** định dạng, múi giờ | ISO-8601 UTC (`2026-09-15T10:00:00Z`) cho mọi trường `*At`/`*_at`; hiển thị múi giờ VN (`+07:00`) do FE tự quy đổi | Chưa có trường thời gian nào ở phạm vi Cart hiện tại (`API_US002-003 §1`); áp dụng cho `Order`/event khi mở rộng |
| 3 | **Phân trang** | Offset (`page`, `pageSize`, mặc định 20, tối đa 100), bọc `{ "data": [...], "pagination": {...} }`; **`GET /v1/cart` không phân trang** (giỏ hàng không có khái niệm trang) | Khớp `04-api-design.md §4.1.1`; `API_US002-003 §1` xác nhận không áp dụng cho Cart |
| 4 | Định dạng phản hồi chung | `{ "data": {...} }` thành công · `{ "error": { "code", "message", "details" }, "traceId" }` lỗi | Khớp `SAD §4.1.1/§4.1.13`, `API_US002-003 §2` |
| 5 | Lỗi nghiệp vụ trả HTTP nào | **4xx**, không phải 200 kèm cờ lỗi | `D5`/`SAD §4.1.13` — tránh bug im lặng |
| 6 | Ngôn ngữ thông điệp lỗi | Trả `code` (client tự dịch theo bảng text SRS §4.2); **không** dựa vào `message` để hiển thị người dùng cuối | `API_US002-003 §2` — BA sở hữu text hiển thị |
| 7 | Khoá idempotency | Header `Idempotency-Key` bắt buộc cho `POST /v1/checkout`, `POST /v1/payments`, `POST /v1/customers/me/loyalty/redeem`; **webhook** dùng khoá nghiệp vụ `gatewayTransactionRef` (không phải header `Idempotency-Key` — bên gửi là đối tác ngoài, không tự đặt header) theo `ADR-004` | `ASR-004`, `04-api-design.md §4.1.1/§4.1.6` |
| 8 | Correlation id | 🔴 **Chưa có tên header chuẩn cho request** — chỉ có `traceId` ở response lỗi (`SAD §4.1.13`). Đề xuất SA: header request `X-Request-Id` (client hoặc gateway sinh nếu thiếu), map 1-1 vào `traceId` xuyên suốt log | `OQ-031` (SA, mới) — Tech Lead xác nhận tên header |
**Bổ sung theo ghi chú người duyệt (không thuộc 8 mục chuẩn của template nhưng bắt buộc chốt ở
GĐ2 icd):**
| # | Vấn đề | Quyết định | `ADR` |
|---|---|---|---|
| 9 | Authn (Customer) | Bearer JWT, TTL access ~15-60 phút + refresh ~7-30 ngày (kế thừa `04-api-design.md §4.2`, chưa xác nhận số cụ thể cho P2) | — *(chưa có ADR riêng, thuộc hoạt động `sec` kế tiếp)* |
| 10 | Authn (Guest) | Header `X-Guest-Session-Id`, giá trị sinh CSPRNG ≥128-bit, cookie `HttpOnly; Secure; SameSite=Lax`, TTL 30 ngày không hoạt động (kế thừa `04-api-design.md §4.1.1`) | — |
| 11 | Authz (ownership check) | Kiểm tra tại **service** (không phải gateway), cache Redis (TTL ngắn), fallback query DB khi cache miss/lỗi | `ADR-007` |
| 12 | Idempotency webhook thanh toán | Khoá = `gatewayTransactionRef`; kèm kiểm tra `timestamp` trong payload lệch ≤5 phút (chống replay, kế thừa `04-api-design.md §4.1.6`) | `ADR-004` |
| 13 | Ordering sự kiện `OrderPlaced`/`PaymentConfirmed` | SQS FIFO, `MessageGroupId = seller_id` | `ADR-005` |
---
## 2. Danh mục interface — `IF-nnn` (17/17 truy được — xem §0.1)
| ID | Từ | Đến | Giao thức | Sync/Async | Ai sở hữu contract | Contract ở đâu | Versioning | Đầu kia hỏng thì sao | Ứng viên `FAIL` |
|---|---|---|---|---|---|---|---|---|---|
| `IF-001` | Web/Mobile Client (Guest/Customer/Seller/Admin) | ALB/API Gateway (`CMP-01`) | HTTPS/TLS 1.2+ | Sync | Platform/DevOps (routing thuần; schema thật ở `IF-002/003/004`) | Không có schema riêng — L7 routing | Theo §1 chung | ALB/WAF down → toàn bộ FE lỗi 503 | ứng viên #14 |
| `IF-002` | Web/Mobile Client | Nhóm Giao dịch (`CMP-02` Identity, `CMP-03` Catalog, `CMP-04` Cart&Order) qua ALB | HTTPS/REST JSON | Sync | BE Nhóm Giao dịch, tách theo module | `contracts/openapi/nhom-giao-dich/{identity,catalog,cart-order}.yaml` *(dự kiến, repo contract chưa tồn tại)* | Path `/v1`, deprecation ≥6 tháng (`04-api-design.md §4.3`) | Timeout/5xx → FE banner theo `AC-US002-03`/`AC-US003-09/10` (xem §6) | ứng viên #1–#3 |
| `IF-003` | Web/Mobile Client (Seller/Admin/CSR/Ops) | Nhóm Hỗ trợ (`CMP-05` Seller Mgmt, `CMP-08` Review, `CMP-10` Shipping) qua ALB | HTTPS/REST JSON | Sync | BE Nhóm Hỗ trợ, tách theo module | `contracts/openapi/nhom-ho-tro/*.yaml` *(dự kiến)* | Path `/v1` | Timeout/5xx → theo màn hình Seller/Admin tương ứng (ngoài phạm vi Cart&Checkout, chưa chi tiết hoá) | ứng viên (chưa đặt số) |
| `IF-004` | Web/Mobile Client (Customer/Guest) | Payment Service (`CMP-11`) qua ALB | HTTPS/REST JSON | Sync | BE Payment (biệt lập) | `contracts/openapi/payment.yaml` *(dự kiến)* | Path `/v1` | Timeout/5xx → FE hiện lỗi thanh toán, không mất dữ liệu giỏ hàng | ứng viên (chưa đặt số) |
| `IF-005` | Cart & Order (`CMP-04`) | Catalog & Inventory (`CMP-03`) | In-process call — cùng ECS task "Nhóm Giao dịch" theo P2 modular monolith (🔴 giả định `ASM-19`, chưa xác nhận `OQ-010`) | Sync | BE Nhóm Giao dịch (module Catalog) | `contracts/internal/catalog-reserve.md` *(dự kiến — module boundary nội bộ, không cần OpenAPI đầy đủ nếu in-process)* | Semver module nội bộ | Không đủ tồn kho → `409 ERR_CONFLICT`; lỗi/timeout nội bộ → checkout thất bại toàn bộ (không tạo `Order`) | ứng viên #1 |
| `IF-006` | Cart & Order (`CMP-04`) | Payment Service (`CMP-11`) | REST/HTTPS — **cross-network** (Payment cô lập mạng/IAM theo `ASR-002`, dù cùng "P2 monolith") | Sync | BE Payment | `contracts/openapi/payment-internal.yaml` *(dự kiến)* | Path `/v1` (internal) | Timeout → checkout thất bại, rollback TX, không tạo `Order`; đơn ở trạng thái chưa commit không hiển thị cho khách | ứng viên #3 |
| `IF-007` | Payment Service (`CMP-11`) | VNPay | REST/HTTPS (init redirect) + Webhook IPN (callback) | Sync (init) + Async (webhook) | VNPay (bên thứ ba) — sàn sở hữu adapter/anti-corruption layer (`ADR-011`) | 🔴 **Cần tài liệu API VNPay thật** — chưa có sandbox (`ARISK-03`). Đường dẫn dự kiến khi có: `contracts/partners/vnpay.yaml` (mirror của tài liệu VNPay, không phải spec do VNPay công bố) | Theo VNPay công bố — chưa xác nhận | Timeout (đề xuất 10s, chưa kiểm chứng) → không có redirect URL, FE hiện lỗi thanh toán, giữ đơn ở `PENDING_PAYMENT` | ứng viên #4 |
| `IF-008` | Payment Service | Momo | Tương tự `IF-007` | Sync (init) + Async (webhook) | Momo (bên thứ ba) | 🔴 **Cần tài liệu API Momo thật** — chưa có sandbox (`ARISK-03`) | Chưa xác nhận | Tương tự `IF-007` | ứng viên #5 |
| `IF-009` | Cart & Order (`CMP-04`) + Payment (`CMP-11`) *(publish)* ↔ Message Backbone (`CMP-12`) ↔ Commission&Payout/Promotion&Loyalty/Review/Notification/Shipping *(consume, ≥5)* | SQS FIFO + EventBridge | Async | Platform/SRE (backbone hạ tầng) + team publish/consume sở hữu schema payload (BE Nhóm Giao dịch/Payment publish; BE Nhóm Hỗ trợ consume) | `contracts/asyncapi/order-events.yaml` *(dự kiến)* — chi tiết §4 | `eventVersion` trong payload, thay đổi breaking → topic/queue mới (không sửa schema đang chạy) | Consumer lỗi lặp lại → DLQ (giữ bao lâu: chưa chốt, xem `FAIL`); publish lỗi → SQS tự retry theo AWS SLA | ứng viên #12/#13 |
| `IF-015` | Cart & Order (`CMP-04`), Identity & Access (`CMP-02`) | ElastiCache Redis (`CMP-15`) | Redis protocol (RESP) | Sync | Ops/SRE (hạ tầng) + BE Nhóm Giao dịch (key schema) | `contracts/internal/redis-keyspace.md` *(dự kiến — quy ước đặt tên key, TTL)* | Không versioning chính thức (naming convention, thay đổi qua migration key) | Cache miss/lỗi kết nối → fallback query DB trực tiếp (`ADR-007`) | ứng viên #11 |
| `IF-016` | Nhóm Giao dịch + Nhóm Hỗ trợ | RDS chính (`CMP-13`) | PostgreSQL wire protocol (SQL) | Sync | Ops/DBA | `db/migrations/*.sql` *(dự kiến, schema-per-module, Flyway/Liquibase chưa chọn)* | Migration versioned tăng dần, không breaking ngược (additive trước, xoá cột ở version sau) | DB chậm/lỗi → 503 + circuit breaker (chưa thiết kế ngưỡng, thuộc `FAIL`) | ứng viên #9 |
| `IF-017` | Payment Service (`CMP-11`) | RDS Payment (`CMP-14`) | PostgreSQL wire protocol | Sync | Ops/DBA (biệt lập, không chung quyền truy cập với `IF-016`) | `db/payment-migrations/*.sql` *(dự kiến)* | Tương tự `IF-016` | Tương tự `IF-016`, ảnh hưởng riêng luồng thanh toán (không lan sang Nhóm Giao dịch/Hỗ trợ) | ứng viên #10 |
| `IF-018` | Shipping & Fulfillment (`CMP-10`) | GHN | REST/HTTPS (tạo/tra vận đơn) + Webhook | Sync + Async | GHN (bên thứ ba) | 🔴 **Cần tài liệu API GHN thật** — chưa có sandbox (`ARISK-04`) | Chưa xác nhận | Timeout (đề xuất 8s) → thử GHTK (fallback chéo, `ASR-013`); cả hai lỗi → đơn ở trạng thái "chờ tạo vận đơn", cảnh báo Ops | ứng viên #6 |
| `IF-019` | Shipping & Fulfillment | GHTK | Tương tự `IF-018`, fallback chéo cho GHN | Sync + Async | GHTK (bên thứ ba) | 🔴 **Cần tài liệu API GHTK thật** — chưa có sandbox (`ARISK-04`) | Chưa xác nhận | Tương tự `IF-018` (fallback ngược sang GHN nếu GHTK cũng lỗi — **chưa thiết kế`, `ARISK-04` chưa kiểm chứng cả hai chiều) | ứng viên #7 |
| `IF-020` | Notification (`CMP-09`) | Email/SMS Provider | REST/HTTPS hoặc SDK qua queue | Async | 🔴🔴 **Chưa chọn nhà cung cấp** (`OQ-016` mở) | N/A — chưa có provider để có contract | N/A | Không gửi được → ghi `NotificationLog.status=failed`, không chặn luồng chính (Notification là consumer bất đồng bộ, không nằm trên đường găng checkout) | ứng viên #8 |
| `IF-021` | Cart & Order (`CMP-04`) | Promotion & Loyalty (`CMP-07`) | REST/JSON — 🔴 **ranh giới mạng chưa rõ**: `SAD §4` legend gọi đây là "internal call cùng process/network boundary", nhưng `SAD §7`/`OQ-026` (TẠM) mô tả Nhóm Giao dịch và Nhóm Hỗ trợ là **2 ECS Fargate service riêng** — hai mô tả này mâu thuẫn nhau (xem `OQ-028` mới) | Sync (theo cả hai cách hiểu) | BE Nhóm Hỗ trợ (module Promotion) | `contracts/openapi/nhom-ho-tro/promotion-apply.yaml` *(dự kiến)* | Path `/v1` (internal) | Lỗi/timeout → hành vi **chưa chốt**: bỏ qua coupon hay chặn checkout (`OQ-017` của BA, `IMPACT_CartCheckout §1.5`) | ứng viên #2 |
| `IF-022` | Cart & Order (`CMP-04`) ↔ Seller Management (`CMP-05`) | REST/JSON nội bộ | Sync | BE Nhóm Hỗ trợ (module Seller) | `contracts/openapi/nhom-ho-tro/seller-lookup.yaml` *(dự kiến)* | Path `/v1` (internal) | Lỗi/timeout → **đề xuất SA**: dùng `sellerName` snapshot đã denormalize trong `CartItem` thay vì gọi runtime (xem §5 mục 1, `OQ-033`) — nếu Tech Lead từ chối denormalize thì cần fallback hiển thị `sellerId` thay tên | ứng viên mới |
🔴 **`IF-003`, `IF-004`, `IF-016`, `IF-017`, `IF-018`, `IF-019`, `IF-020`** chỉ được xác nhận ở
mức catalog (bảng trên) trong lượt chạy này — không thuộc phạm vi "chi tiết đầy đủ Cart & Order"
của ghi chú người duyệt. Chi tiết đầy đủ (mã lỗi, ví dụ JSON, quyền chi tiết) để hoạt động `icd`
lần sau hoặc khi module tương ứng tới lượt là trọng tâm.
🔴 **`CMP-04` và `CMP-05` trong `SAD §4.1` đều liệt kê `IF-022` ở cột "Interface ra"** — không rõ
chiều gọi thật (Cart&Order gọi Seller Mgmt, hay ngược lại, hay cả hai chiều dùng chung một ID).
ICD tạm giả định chiều **Cart&Order → Seller Management** (khớp nhu cầu lấy `sellerName` ở
`GET /v1/cart`) và ghi `OQ-032` (SA, mới) để Tech Lead xác nhận khi rà lại `SAD`.
### 2.1 Interface với hệ thống ngoài
| ID | Hệ thống | Ai liên hệ được | SLA của họ | Giới hạn tốc độ | Cơ chế xác thực | Môi trường thử | Đã gọi thử chưa |
|---|---|---|---|---|---|---|---|
| `IF-007` | VNPay | 🔴 Chưa có đầu mối liên hệ chính thức (`CON-04`, `ARISK-03`) | 🔴 Chưa xác nhận | 🔴 Chưa xác nhận | Checksum/chữ ký theo tài liệu VNPay (chưa xác minh chi tiết) | Không | ☐ |
| `IF-008` | Momo | 🔴 Chưa có đầu mối liên hệ chính thức | 🔴 Chưa xác nhận | 🔴 Chưa xác nhận | Chữ ký theo tài liệu Momo (chưa xác minh chi tiết) | Không | ☐ |
| `IF-018` | GHN | 🔴 Chưa có đầu mối liên hệ chính thức (`ARISK-04`) | 🔴 Chưa xác nhận | 🔴 Chưa xác nhận | Token/chữ ký theo tài liệu GHN (chưa xác minh) | Không | ☐ |
| `IF-019` | GHTK | 🔴 Chưa có đầu mối liên hệ chính thức | 🔴 Chưa xác nhận | 🔴 Chưa xác nhận | Token/chữ ký theo tài liệu GHTK (chưa xác minh) | Không | ☐ |
| `IF-020` | Email/SMS Provider | 🔴🔴 Chưa chọn nhà cung cấp (`OQ-016`) | N/A | N/A | N/A | Không | ☐ |
🔴 **Cả 5 hệ thống ngoài đều "chưa có sandbox" hoặc "chưa chọn nhà cung cấp"** — thiết kế phải giả
định chúng **có thể hỏng bất cứ lúc nào** (không có SLA để dựa vào). Đã ghi `ARISK-03` (VNPay/
Momo), `ARISK-04` (GHN/GHTK). Email/SMS chưa có `ARISK` riêng cho việc "chưa chọn nhà cung cấp" —
đã có ở `CTX §4.2` (mức 🔴🔴). Không thiết kế thêm timeout/retry cụ thể mới ở đây ngoài số đã kế
thừa (`CTX §4.2`: VNPay/Momo 10s, GHN/GHTK 8s, Email/SMS 5s) — số này **chưa kiểm chứng sandbox
thật**, giữ nguyên trạng cho tới khi có tài liệu đối tác.
---
## 3. Chi tiết từng interface
### 3.1 `IF-002` — Client ↔ Nhóm Giao dịch: **Cart & Order** (US-002/US-003)
*Đây là phần "chi tiết đầy đủ" theo yêu cầu — 4 endpoint dưới đây cùng thuộc `IF-002` (ranh giới
Client → Nhóm Giao dịch), tách theo endpoint để xác nhận `API_US002-003_v1.0.md` của BA.*
| | |
|---|---|
| **Hai đầu** | Web/Mobile Client (Guest/Customer) → Cart & Order Service (`CMP-04`, trong Nhóm Giao dịch) |
| **Giao thức** | HTTPS/REST JSON, qua ALB (`CMP-01`, không xử lý authz chi tiết — chỉ chuyển token/session xuống, theo `SAD §4.1` cột "cái `CMP-01` KHÔNG làm") |
| **Đồng bộ?** | Sync · timeout đề xuất: chưa chốt ở `ICD` (thuộc hoạt động `fail`), tạm dùng ngân sách latency `QAS-003`/`QAS-004` làm giới hạn trên thực tế |
| **Authn** | Bearer JWT (Customer) hoặc `X-Guest-Session-Id` (Guest) — theo §1 mục 9/10 |
| **Authz** | Ownership check tại service (không phải gateway) + cache Redis, theo `ADR-007`; Guest theo `session_id`, Customer theo `customer_id` — khớp `RBAC_CartCheckout §2` (BA). Không cần scope riêng ngoài định danh hợp lệ (khớp `API_US002-003 §3.1`) |
| **Idempotent** | `GET`: có (đọc). `PATCH`/`DELETE`: **không cần** `Idempotency-Key` — thao tác tự nhiên idempotent theo semantics (PATCH set quantity tuyệt đối; DELETE trên item đã xoá trả `404`, không side-effect kép). `POST /v1/checkout`: **có**, `Idempotency-Key` bắt buộc (theo §1 mục 7, `04-api-design.md §4.1.1`) |
| **Tần suất dự kiến** | `GET /v1/cart`: đọc nhiều (mỗi lần mở SCR-04); `PATCH`/`DELETE`: ghi đơn lẻ theo thao tác người dùng — nguồn: `QAS-003`/`QAS-004` (chưa có con số req/s tuyệt đối, chỉ có ngưỡng latency) |
| **`QAS` áp dụng** | `QAS-003` (`GET /v1/cart` p95 ≤500ms 🔴 đề xuất, `OQ-018`), `QAS-004` (`PATCH`/`DELETE` p95 ≤300ms 🔴 đề xuất, `OQ-019`), `QAS-010` (0% IDOR thành công), `QAS-002` (`POST /v1/checkout` p95 ≤3s, `Must`) |
**Bảng endpoint × xác nhận/điều chỉnh `API_US002-003_v1.0.md`** — xem chi tiết đầy đủ ở §5.
#### 3.1.1 `GET /v1/cart`
| | |
|---|---|
| **Mục đích** | Tải dữ liệu SCR-04 (US-002) |
| **Quyền** | Guest (`X-Guest-Session-Id`) hoặc Customer (Bearer JWT) — không scope riêng |
| **Idempotent** | Có (read-only) |
**Response 200 — XÁC NHẬN có điều chỉnh** *(so với đề xuất BA §3.1)*
```json
{
"data": {
"cartId": "cart_123",
"status": "active",
"sellers": [
{
"sellerId": "seller_11",
"sellerName": "Shop A",
"subtotalVnd": 325000,
"items": [
{
"cartItemId": "citem_001",
"productVariantId": "variant_789",
"productName": "Áo thun basic",
"variantLabel": "Size M, Đỏ",
"unitPriceSnapshotVnd": 100000,
"currentPriceVnd": 100000,
"priceChanged": false,
"quantity": 2,
"imageUrl": "https://cdn.example.com/p/789.jpg"
}
]
}
],
"totalVnd": 585000,
"totalItemCount": 5
}
}
```
| Trường | Kiểu | Khác gì so với đề xuất BA | Lý do |
|---|---|---|---|
| `sellers[].sellerName` | string | **Giữ nguyên đề xuất BA** (trả sẵn) nhưng **đổi nguồn dữ liệu**: đề xuất SA — denormalize snapshot vào `CartItem` lúc thêm giỏ (giống `unit_price_snapshot`), không gọi runtime `IF-022` mỗi request | Gọi `IF-022` (Cart&Order→Seller Mgmt) mỗi lần `GET /v1/cart` cạnh tranh trực tiếp ngân sách `QAS-003` (≤500ms) — cùng loại xung đột đã ghi ở `QAS §A4` dòng 1 cho ownership check. `OQ-033` (SA, mới) — cần Tech Lead + DBA xác nhận trước khi đưa vào `DAT` |
| `items[].unitPriceSnapshotVnd`, `currentPriceVnd`, `priceChanged` | number, number, boolean | **Điều chỉnh (thêm trường)** so với đề xuất BA chỉ có `unitPriceVnd` | `OQ-029` (BA) chưa chốt giá hiển thị là snapshot hay real-time — thêm cả hai trường (additive, không breaking) để SCR-04 hoạt động đúng dù PO trả lời theo hướng nào; `priceChanged = currentPriceVnd != unitPriceSnapshotVnd`. Nếu PO xác nhận chỉ cần 1 trong 2, có thể bỏ trường thừa ở version sau (không breaking vì chỉ xoá field không dùng) |
| `sellers[].subtotalVnd`, `totalVnd` | number | **Xác nhận** — BE tính sẵn, không để FE tự cộng | Tránh sai lệch làm tròn FE/BE (đúng lý do BA đưa ra) |
| Cấu trúc nhóm theo `sellers[].items[]` | — | **Xác nhận** — đây chính là câu trả lời cho `OQ-034(BA)`: trả về đã nhóm theo seller, **không** trả danh sách phẳng | Nhất quán trực tiếp với mô hình `Order`/`OrderSeller` đã chốt ở `ADR-003` — nếu Cart không nhóm theo seller từ đầu, `POST /v1/checkout` phải tự nhóm lại phía BE mà FE không thấy trước, gây lệch trải nghiệm giữa xem giỏ và xác nhận đơn. **Đề nghị BA đóng `OQ-034` trong sổ BA, tham chiếu `IF-002`/`ADR-003`.** |
**Mã lỗi** — xác nhận nguyên trạng theo `API_US002-003 §3.1` (`401 ERR_AUTH_INVALID_TOKEN`,
`500/503 ERR_INTERNAL`/`ERR_SERVICE_UNAVAILABLE`, mã SRS `E-CART-0004`).
#### 3.1.2 `PATCH /v1/cart/items/{cartItemId}`
| | |
|---|---|
| **Mục đích** | Cập nhật `quantity` (US-003 F01) |
| **Quyền** | Chủ sở hữu `CartItem` — Guest theo `session_id`, Customer theo `customer_id` |
**XÁC NHẬN** request/response body theo đề xuất BA §3.2 nguyên trạng (`{ "quantity": 3 }` →
`{ "cartItemId", "quantity", "sellerSubtotalVnd", "cartTotalVnd" }`) — trả `CartItem` đã cập nhật
+ tổng nhóm/tổng giỏ liên quan, **không** trả toàn bộ `Cart`. Lý do: nhẹ hơn, đủ dữ liệu để FE cập
nhật UI dòng đang sửa + tổng, khớp cách BA đã đề xuất ở §5 mục 3.
**XÁC NHẬN — đóng `OQ-032(BA)`:** cơ chế ownership của Guest dùng **cùng** cơ chế `403
ERR_FORBIDDEN_OWNERSHIP` như Customer (đối chiếu `session_id` thay vì `customerId`), theo `ADR-007`
(chỗ ra quyết định là service, có cache) — không có cơ chế riêng biệt cho Guest. **Đề nghị BA đóng
`OQ-032` trong sổ BA, tham chiếu `ADR-007`.**
**CHỜ OQ** — mục 7 của BA (`PATCH` có kiểm tra tồn kho tại đây không, trả `422 ERR_BUSINESS_RULE`):
đây là quyết định phạm vi nghiệp vụ thuộc PO/Tech Lead (`OQ-030(BA)`), không phải quyết định kiến
trúc — ICD **không tự quyết**. Ghi chú kiến trúc cho cả hai nhánh: nếu PO chọn "có kiểm tra", `PATCH`
gọi lại `IF-005` (đã có sẵn, không cần interface mới); nếu "không", giữ thiết kế hiện tại.
Mã lỗi: xác nhận nguyên trạng `API_US002-003 §3.2` (`400 ERR_VALIDATION`, `403
ERR_FORBIDDEN_OWNERSHIP`, `404 ERR_NOT_FOUND`, `409 ERR_CONFLICT`, `500/503`). Mã `422
ERR_BUSINESS_RULE` để dự phòng, chỉ kích hoạt nếu `OQ-030(BA)` trả lời "có kiểm tra".
#### 3.1.3 `DELETE /v1/cart/items/{cartItemId}`
**XÁC NHẬN** nguyên trạng đề xuất BA §3.3: `200` kèm body (`deletedCartItemId`,
`sellerSubtotalVnd`, `cartTotalVnd`, `sellerRemoved`), không dùng `204`. Lý do: giữ nhất quán với
`PATCH` (đều trả tổng mới để FE khỏi tự tính), và `sellerRemoved` cần thiết để FE biết ẩn cả nhóm
seller khi giỏ hàng của seller đó rỗng — đúng đề xuất BA §5 mục 4. Cùng cơ chế ownership như
`PATCH` (xem 3.1.2).
#### 3.1.4 `POST /v1/checkout` *(không thuộc `API_US002-003` của BA nhưng nằm trong `IF-002`, cần cho luồng Cart&Order đầy đủ)*
| | |
|---|---|
| **Mục đích** | Tạo `Order`/`OrderSeller` từ `Cart`, khởi tạo thanh toán (`PROCESS_CartCheckout` B5-B8) |
| **Idempotent** | Có — `Idempotency-Key` bắt buộc |
| **`QAS` áp dụng** | `QAS-002` (p95 ≤3s, Must) |
Request/response theo ví dụ đã có ở `04-api-design.md §4.1.5` (`cartId`, `shippingAddressId`,
`paymentMethod`, `couponCode` → `parentOrderId`, `orders[]`, `paymentRedirectUrl`) — **xác nhận
kế thừa nguyên trạng**, chưa có `AC`/`SRS` riêng của BA cho US-004 (Checkout) trong phạm vi input
đã cung cấp ở lượt chạy này nên không có gì để "điều chỉnh"; nếu BA viết `SRS`/`API` riêng cho
US-004 sau, `ICD` sẽ xác nhận lại tại lượt `icd` kế tiếp.
---
### 3.2 `IF-005` — Cart & Order → Catalog & Inventory (kiểm tra/giữ tồn kho)
| | |
|---|---|
| **Mục đích** | Kiểm tra tồn kho + giữ chỗ (reserve) khi checkout; có thể tái dùng cho `PATCH /v1/cart/items` nếu `OQ-030(BA)` chọn kiểm tra tại đây |
| **Đồng bộ?** | Sync, in-process (cùng ECS task theo `ASM-19`, chưa xác nhận `OQ-010`) |
| **Idempotent** | Không cần — mỗi lần gọi là một hành động "giữ chỗ" mới, có `reservationId` riêng (đề xuất SA, chưa xác nhận) |
| **`QAS` áp dụng** | `QAS-002` (nằm trong ngân sách 3s tổng của checkout) |
| **Lỗi thì sao** | Không đủ tồn kho → trả `409 ERR_CONFLICT`, Cart & Order trả `409` cho client (B4, yêu cầu điều chỉnh giỏ) |
---
### 3.3 `IF-006` — Cart & Order → Payment Service (khởi tạo thanh toán)
| | |
|---|---|
| **Mục đích** | Khởi tạo `Payment` cho (các) `OrderSeller` vừa tạo trong một lần checkout |
| **Đồng bộ?** | Sync, cross-network (Payment cô lập mạng theo `ASR-002`) |
| **Idempotent** | Có — truyền lại `Idempotency-Key` gốc của `POST /v1/checkout` |
| **`QAS` áp dụng** | `QAS-002` (nằm trong ngân sách 3s) |
**Request (đề xuất SA — chưa xác nhận Tech Lead)**
```json
{
"orderSellers": [
{ "orderSellerId": "order_001", "sellerId": "seller_11", "amountVnd": 350000 },
{ "orderSellerId": "order_002", "sellerId": "seller_22", "amountVnd": 120000 }
],
"paymentMethod": "VNPAY",
"customerId": "cust_123"
}
```
🔴 **Vì sao cần `sellerId` ở đây:** Payment Service phải biết `seller_id` của từng `OrderSeller`
để publish sự kiện `PaymentConfirmed` đúng `MessageGroupId` theo `ADR-005` (per-seller FIFO) —
nếu không truyền tại lúc khởi tạo, Payment Service phải gọi ngược lại Cart & Order để tra cứu,
tạo thêm một phụ thuộc vòng. Đây là đề xuất kiến trúc mới của `ICD`, ghi `OQ-030` (SA) để Tech
Lead xác nhận trước khi dùng làm chuẩn thi công.
**Response:** `gatewayTransactionRef` (một hoặc nhiều, tuỳ 1 payment cho nhiều `OrderSeller` hay
từng `OrderSeller` một payment riêng — **chưa chốt**, phụ thuộc quyết định nghiệp vụ "1 lần thanh
toán cho N seller" đã ngụ ý ở `04-api-design.md §4.1.5` ví dụ `paymentRedirectUrl` số ít). Ghi
nhận là điểm cần Tech Lead xác nhận cùng `OQ-030`.
**Lỗi thì sao:** Timeout/lỗi → Cart & Order rollback transaction, không tạo `Order`/`OrderSeller`
(theo sequence diagram `SAD §6.1`).
---
### 3.4 `IF-007`/`IF-008` — Payment Service ↔ VNPay/Momo
| | |
|---|---|
| **Init (sync)** | `POST` tới endpoint khởi tạo giao dịch của đối tác — **schema thật chưa biết** (🔴 cần tài liệu API VNPay/Momo, `ARISK-03`) |
| **Webhook (async)** | `POST /v1/payments/webhooks/{vnpay,momo}` (theo `04-api-design.md §4.1.6`) — đối tác gọi vào, xác thực chữ ký/checksum theo tài liệu của họ (chưa xác minh chi tiết) |
| **Idempotency (`ADR-004`)** | Khoá = `gatewayTransactionRef`; kiểm tra `timestamp` payload lệch ≤5 phút (chống replay); đã xử lý trước đó → trả `200 OK` không xử lý lại nghiệp vụ |
| **`QAS` áp dụng** | `QAS-002` (init, trong ngân sách 3s checkout), `QAS-014` (đối soát ≤15 phút 🔴 đề xuất, `OQ-021`) |
| **Timeout đề xuất** | 10s (kế thừa `CTX §4.2`, **chưa kiểm chứng sandbox thật**) |
| **Đầu kia hỏng thì sao** | Init timeout → không có `paymentRedirectUrl`, FE hiện lỗi thanh toán (giữ `Order` ở `PENDING_PAYMENT`, không mất giỏ hàng); webhook mất/trễ → job đối soát bù phát hiện lệch trong ≤15 phút (`ADR-004`) |
🔴 **Không bịa schema request/response thật của VNPay/Momo** — mọi trường cụ thể (`vnp_TxnRef`,
`vnp_SecureHash`, mã lỗi của VNPay, v.v.) **chưa được ghi** vì chưa có tài liệu/sandbox chính thức.
Xem `OQ-014` (biểu phí, đã có), bổ sung nhu cầu tài liệu kỹ thuật ở §8.
---
### 3.5 `IF-015` — Cart & Order/Identity & Access → Redis (ownership/session cache)
| | |
|---|---|
| **Mục đích** | Cache session (Guest)/JWT claims đã xác thực (Customer) + ownership `Cart`/`CartItem` để tránh query DB mỗi request (`ADR-007`) |
| **Key đề xuất** | `session:{session_id}`, `ownership:cart_item:{cartItemId}` → `{ownerType, ownerId}` (đề xuất SA, chưa xác nhận) |
| **TTL đề xuất** | Theo TTL session Guest (30 ngày) cho `session:*`; ngắn hơn (VD 5 phút, chưa chốt) cho `ownership:*` để giảm rủi ro dữ liệu cũ khi giỏ hàng đổi chủ (hiếm khi xảy ra nhưng phải xử lý) |
| **Lỗi thì sao** | Cache miss/Redis lỗi → fallback query DB trực tiếp (không chặn luồng, chỉ chậm hơn) — chi tiết đầy đủ (retry, circuit breaker) thuộc hoạt động `fail` |
---
### 3.6 `IF-021` — Cart & Order → Promotion & Loyalty (áp dụng coupon/điểm)
| | |
|---|---|
| **Mục đích** | Tính giá trị giảm khi checkout (và có thể cả `PATCH`/`GET` nếu cần hiển thị giảm giá tạm thời — **chưa chốt phạm vi**) |
| **Ranh giới mạng** | 🔴 **Chưa rõ** — xem cảnh báo ở bảng §2 (`OQ-028`, mới) |
| **Lỗi thì sao** | **Chưa chốt** — phụ thuộc `OQ-017` của BA (`IMPACT_CartCheckout §1.5`): bỏ qua coupon và tiếp tục checkout, hay chặn checkout khi Promotion&Loyalty lỗi. `ICD` không tự quyết (thuộc PO/Tech Lead) |
| **`QAS` áp dụng** | `QAS-002` (nằm trong ngân sách 3s checkout — nếu là cross-network call thật, đây là một round-trip mạng thêm cần tính vào ngân sách) |
---
### 3.7 `IF-022` — Cart & Order ↔ Seller Management (tra cứu tên seller)
| | |
|---|---|
| **Mục đích** | Cung cấp `sellerName` cho `GET /v1/cart` (xem §3.1.1, §5 mục 1) |
| **Đề xuất SA** | **Không gọi runtime mỗi request** — denormalize `sellerName` snapshot vào `CartItem` lúc thêm vào giỏ (tương tự `unit_price_snapshot`); `IF-022` chỉ được gọi khi thêm sản phẩm vào giỏ (`POST /v1/cart/items`, ngoài phạm vi chi tiết US-002/003 lần này) hoặc khi cần đồng bộ lại (batch job, chưa thiết kế) |
| **Hệ quả nếu Tech Lead từ chối denormalize** | Mỗi `GET /v1/cart` phải gọi `IF-022` runtime — cần đo lại `QAS-003` (giống xung đột đã ghi ở `QAS §A4` dòng 1 cho ownership), có thể cần cache riêng cho `sellerName` (TTL dài hơn vì tên seller ít đổi) |
| **Rủi ro dữ liệu cũ** | Nếu seller đổi tên gian hàng sau khi khách đã thêm vào giỏ, `sellerName` hiển thị có thể lệch tới khi giỏ được làm mới — chấp nhận được vì tần suất đổi tên seller thấp và không ảnh hưởng tính đúng đắn giao dịch (chỉ hiển thị) |
---
## 4. Hợp đồng sự kiện — `OrderPlaced` / `PaymentConfirmed`
*Theo `ADR-005` (SQS FIFO, `MessageGroupId = seller_id`) và `ASR-005` (≥5 consumer). Schema dưới
đây là **đề xuất SA**, chưa được Tech Lead xác nhận (`OQ-030`) — không phải chuẩn đã chốt.*
| Sự kiện | Nhà phát | Người nhận | Schema | Thứ tự có quan trọng | At-least-once? | Trùng thì sao |
|---|---|---|---|---|---|---|
| `OrderPlaced` | Cart & Order (`CMP-04`) | Commission&Payout, Promotion&Loyalty, Review, Notification, Shipping&Fulfillment (5 consumer, `ASR-005`) | Xem bên dưới | Có — trong cùng `seller_id` (không cần thứ tự toàn cục) | Có (SQS FIFO/EventBridge) | Khử trùng theo `eventId` (SQS FIFO `MessageDeduplicationId`, cửa sổ 5 phút) **+** consumer tự upsert theo khoá nghiệp vụ `(orderSellerId, eventType)` để chống trùng ngoài cửa sổ 5 phút (VD replay thủ công) |
| `PaymentConfirmed` | Payment Service (`CMP-11`) | Cart & Order (cập nhật `OrderSeller.status`), Commission&Payout, Notification | Xem bên dưới | Có — trong cùng `seller_id` | Có | Tương tự trên |
**Schema `OrderPlaced` (đề xuất SA):**
```json
{
"eventId": "evt_9f8a7b6c",
"eventType": "OrderPlaced",
"eventVersion": "1.0",
"occurredAt": "2026-09-15T10:00:00Z",
"messageGroupId": "seller_11",
"data": {
"orderId": "order_parent_789",
"orderSellerId": "order_001",
"sellerId": "seller_11",
"customerId": "cust_123",
"guestSessionId": null,
"items": [
{ "productVariantId": "variant_789", "quantity": 2, "unitPriceVnd": 100000 }
],
"subtotalVnd": 325000,
"shippingAddressSnapshot": {
"recipientName": "Nguyễn Văn A",
"phone": "09xxxxxxxx",
"addressLine": "..."
},
"paymentMethod": "VNPAY",
"status": "PENDING_PAYMENT"
}
}
```
🔴 **`shippingAddressSnapshot` chỉ chứa trường tối thiểu cần giao hàng** (data-minimization theo
`ASR-008`) — danh sách trường whitelist cụ thể **chưa được Pháp chế/Security rà soát** (`OQ-007`
vẫn mở). Không thêm trường PII khác vào payload event này cho tới khi có xác nhận.
**Schema `PaymentConfirmed` (đề xuất SA):**
```json
{
"eventId": "evt_1a2b3c4d",
"eventType": "PaymentConfirmed",
"eventVersion": "1.0",
"occurredAt": "2026-09-15T10:05:00Z",
"messageGroupId": "seller_11",
"data": {
"orderId": "order_parent_789",
"orderSellerId": "order_001",
"sellerId": "seller_11",
"gatewayTransactionRef": "vnpay_txn_xxx",
"paymentMethod": "VNPAY",
"amountVnd": 325000,
"confirmedAt": "2026-09-15T10:04:50Z"
}
}
```
| Vấn đề | Quyết định |
|---|---|
| Message hỏng (poison message) | DLQ riêng theo queue; **thời gian giữ chưa chốt** — thuộc hoạt động `fail`. Ai xử lý: SRE + Tech Lead module tương ứng (chưa phân công cụ thể) |
| Đọc lại từ đầu (replay) | 🔴 Chưa chốt — SQS không hỗ trợ replay tự nhiên như Kafka; nếu cần, phải có cơ chế lưu event log riêng (ngoài phạm vi quyết định `ADR-005` hiện tại) |
| Thứ tự đảm bảo trong phạm vi nào | Trong cùng `seller_id` (message group), không đảm bảo thứ tự giữa các seller khác nhau — đúng thiết kế `ADR-005` |
| Versioning schema | `eventVersion` field; thay đổi additive (thêm field) không tăng version; breaking change (đổi kiểu/xoá field bắt buộc) → `eventVersion` mới + queue/topic mới, giữ producer cũ chạy song song tối thiểu theo chính sách chung §1 (khuyến nghị áp dụng tương tự 6 tháng của REST, chưa chính thức hoá cho event) |
---
## 5. Đối chiếu với `API_US002-003_v1.0.md` của bộ BA
| Endpoint / mục | `IF-nnn` | Khớp? | Xác nhận / Điều chỉnh / Chờ OQ | Hành động cho BA |
|---|---|---|---|---|
| `GET /v1/cart` — cấu trúc chung | `IF-002` §3.1.1 | 🟡 Điều chỉnh | **Xác nhận** cấu trúc nhóm theo seller (đóng `OQ-034(BA)`) | BA cập nhật `API_US002-003 §3.1` — bỏ dấu `⚠️ đề xuất`, ghi "SA xác nhận qua `ICD IF-002`, đóng `OQ-034`" |
| `GET /v1/cart` — `sellers[].sellerName` | `IF-002`/`IF-022` | 🟡 Điều chỉnh | **Điều chỉnh** — vẫn trả sẵn nhưng nguồn là denormalize snapshot, không join runtime | BA cập nhật `API_US002-003 §5` mục 1: "SA điều chỉnh — xem `ICD §3.7`, chờ `OQ-033`" |
| `GET /v1/cart` — `sellers[].subtotalVnd`, `totalVnd` | `IF-002` | ✅ Khớp | **Xác nhận** — BE tính sẵn | BA đánh dấu ✅ tại `API_US002-003 §5` mục 2 |
| `GET /v1/cart` — `items[].unitPriceVnd` | `IF-002` | 🟡 Điều chỉnh | **Điều chỉnh** — tách thành `unitPriceSnapshotVnd` + `currentPriceVnd` + `priceChanged` (additive) | BA cập nhật `SRS_US002-003` field mapping C06 + `API §5` — tham chiếu `OQ-029`, ghi rõ 2 trường mới không breaking |
| `PATCH /v1/cart/items/{id}` — request/response | `IF-002` §3.1.2 | ✅ Khớp | **Xác nhận** nguyên trạng đề xuất BA §3.2/§5 mục 3 | BA đánh dấu ✅ |
| `PATCH` — ownership Guest | `IF-002` | ✅ Khớp | **Xác nhận** — đóng `OQ-032(BA)` | BA cập nhật `API §7`/`SRS §8` — đánh dấu đã đóng, tham chiếu `ADR-007` |
| `PATCH` — kiểm tra tồn kho (`422`) | `IF-002`/`IF-005` | ➖ Chờ | **Chờ OQ** — `OQ-030(BA)` là quyết định phạm vi nghiệp vụ (PO/Tech Lead), không phải kiến trúc | Không hành động thêm — BA/PO tự xử lý `OQ-030(BA)`, kiến trúc đã sẵn sàng cho cả hai nhánh |
| `DELETE /v1/cart/items/{id}` — response `200`+body | `IF-002` §3.1.3 | ✅ Khớp | **Xác nhận** nguyên trạng đề xuất BA §3.3/§5 mục 4 | BA đánh dấu ✅ |
| Quy ước `id` dạng `string` | §1 mục 1 | ✅ Khớp | **Xác nhận** — khớp §5 mục 5 của BA | BA đánh dấu ✅ |
| Định dạng lỗi `{error:{code,message,details}, traceId}` | §1 mục 4/6 | ✅ Khớp | **Xác nhận** | Không hành động |
**Endpoint trong `API` không có `IF-nnn` riêng:** không có — cả 3 endpoint của `API_US002-003`
đều thuộc `IF-002` (đã chi tiết hoá theo endpoint ở §3.1). `POST /v1/checkout` (US-004, ngoài
phạm vi `API_US002-003`) cũng thuộc `IF-002`, xác nhận ở §3.1.4.
---
## 6. Hành vi giao diện khi API lỗi
*Xác nhận nguyên trạng bảng của `API_US002-003 §6` — không lệch, không điều chỉnh.*
| Tình huống | Giao diện làm gì | `AC` |
|---|---|---|
| 401 hết phiên (Customer) | Chuyển về đăng nhập, giữ đường dẫn quay lại `/cart` | `AC-US002-06` |
| 403 (ownership) | Chuyển trang "Không có quyền", không hiển thị dữ liệu | `AC-US003-12`, `13` |
| 404/409 (item đã bị xoá) | Banner tại dòng, sau đó ẩn dòng, cập nhật lại tổng | `AC-US003-11` |
| 5xx/timeout khi tải (`GET`) | Banner C12 thay chỗ danh sách, nút "Thử lại" | `AC-US002-03` |
| 5xx/timeout khi sửa/xoá | Banner tại dòng, giữ nguyên dữ liệu trước đó, mở lại nút | `AC-US003-09`, `10` |
| Đang gửi `PATCH`/`DELETE` | Khoá đúng dòng đang xử lý, không khoá toàn trang | — |
---
## 7. Giả định & Ngoài phạm vi
**Giả định** *(tiếp số toàn dự án — `ASM-01…23` đã dùng, bắt đầu `ASM-24`)*:
| ID | Giả định | Cách xác minh | Nếu sai |
|---|---|---|---|
| `ASM-24` | `IF-021` (Cart&Order → Promotion&Loyalty) là cross-network call (2 ECS service riêng) chứ không phải in-process, theo hướng TẠM `OQ-026`/`DEC-16` | Tech Lead xác nhận khi trả lời `OQ-026`/`OQ-028` | `IF-021` đổi từ REST sang in-process call — ảnh hưởng thiết kế `FAIL` (không cần timeout/circuit breaker riêng nữa) và ngân sách latency `QAS-002` (giảm 1 network hop) |
| `ASM-25` | `IF-022` có chiều gọi là Cart&Order → Seller Management (không phải ngược lại) | Tech Lead xác nhận khi rà `SAD §4.1` (`OQ-032`) | Nếu ngược lại, thiết kế denormalize `sellerName` ở §3.7/§5 không còn hợp lý — cần thiết kế lại nguồn dữ liệu |
| `ASM-26` | Payment Service nhận đủ `sellerId` của từng `OrderSeller` ngay tại `IF-006` (không cần tra cứu ngược) để publish `PaymentConfirmed` đúng `MessageGroupId` | Tech Lead xác nhận thiết kế request `IF-006` ở §3.3 (`OQ-030`) | Nếu Payment Service không nhận `sellerId` lúc khởi tạo, cần thêm một lời gọi ngược `IF-mới` (Payment → Cart&Order) trước khi publish event — thêm 1 network hop vào đường găng xác nhận thanh toán |
**Ngoài phạm vi:**
- Chi tiết đầy đủ (mã lỗi, ví dụ JSON, quyền chi tiết) cho `IF-003`, `IF-004`, `IF-016`, `IF-017`,
`IF-018`, `IF-019`, `IF-020` — chỉ có ở mức catalog (§2) trong lượt chạy này; không thuộc phạm
vi "Cart & Order" của ghi chú người duyệt.
- Schema request/response thật của VNPay/Momo/GHN/GHTK — không bịa; chờ tài liệu/sandbox đối tác
thật (`ARISK-03/04`).
- Thiết kế DLQ, circuit breaker, backoff cụ thể cho mọi `IF-nnn` — thuộc hoạt động `fail` (hoạt
động 8, chưa chạy); ICD chỉ ghi timeout đề xuất kế thừa và tham chiếu ứng viên `FAIL`.
- Endpoint `POST /v1/cart/items` (thêm sản phẩm vào giỏ, FR-05) — có trong `04-api-design.md` và
cần cho `IF-022` (thời điểm denormalize `sellerName`) nhưng **không** có `SRS`/`AC`/`API` riêng
của BA trong phạm vi input đã cung cấp (`API_US002-003` chỉ có US-002/003: xem/sửa/xoá) — chưa
chi tiết hoá đầy đủ ở lượt này, chỉ nhắc tới khi cần giải thích nguồn snapshot.
- Rà soát lại con số "22 `IF-nn`" của `SAD` — xem §0.1, để lần cập nhật `SAD` kế tiếp, không phải
việc của `ICD`.
---
## 8. Open Questions
*Tiếp số sổ SA (`00-index/OQ_e-commerce.md` đang ở `OQ-027`) — bắt đầu `OQ-028`.*
| ID | Câu hỏi | Hỏi ai | Từ ngày | Chặn gì | Hệ quả nếu trả lời ngược |
|---|---|---|---|---|---|
| `OQ-028` | `IF-021` (Cart&Order → Promotion&Loyalty) là in-process call (theo `SAD §4` legend) hay cross-network REST (theo `OQ-026` TẠM: 2 ECS service riêng)? Hai mô tả trong `SAD` mâu thuẫn nhau. | Tech Lead | 2026-09-15 | Thiết kế `FAIL` cho `IF-021`; ngân sách latency `QAS-002` | Nếu cross-network: cần thêm timeout/retry/circuit breaker riêng cho `IF-021`, tăng rủi ro trễ ở bước áp coupon của checkout; nếu in-process: không cần, nhưng phải sửa lại `SAD §7` deployment view (đang vẽ 2 ECS service tách biệt hoàn toàn) |
| `OQ-029` | `SAD §4` (header/Change Log/§Tự chấm) nêu "22 `IF-nn` ứng viên" nhưng chỉ 17 ID riêng biệt xuất hiện trong nội dung — có 5 `IF` nào bị bỏ sót khi viết `SAD`, hay con số "22" là đếm nhầm? | SA (rà lại ở lần cập nhật `SAD` kế tiếp) | 2026-09-15 | Độ chính xác của `SAD §4`/`§Tự chấm`; không chặn `AG2` (đã có 17 interface đủ bao phủ mọi cặp container/hệ ngoài xuất hiện trong văn bản) | Nếu có 5 interface thật bị bỏ sót: `SAD`/`ICD` cần bổ sung — có thể ảnh hưởng `sa-conformance` coverage nếu `ASR`/`CMP` liên quan tới các interface đó chưa được phục vụ |
| `OQ-030` | Xác nhận thiết kế schema sự kiện `OrderPlaced`/`PaymentConfirmed` (§4) và request `IF-006` (§3.3, đặc biệt việc Payment Service nhận `sellerId` ngay lúc khởi tạo) — đây là đề xuất mới của `ICD`, chưa có nguồn nào khác xác nhận. | Tech Lead | 2026-09-15 | Thi công Cart&Order/Payment/message backbone; `DAT` (hoạt động 5, ownership `ReconciliationLog`/event log) | Nếu Tech Lead chọn thiết kế khác (VD 1 payment cho mỗi `OrderSeller` riêng thay vì có thể gộp): cần sửa lại ví dụ request/response `IF-006` và số lượng `paymentRedirectUrl` trả về ở `POST /v1/checkout` |
| `OQ-031` | Tên header chuẩn cho correlation id ở request là gì? Đề xuất SA: `X-Request-Id`. | Tech Lead | 2026-09-15 | Chuẩn hoá logging/observability xuyên suốt hệ thống (input cho hoạt động `inf`) | Nếu chọn tên khác hoặc không cần header riêng (chỉ dựa vào `traceId` sinh phía server): cập nhật lại §1 mục 8, không ảnh hưởng lớn (chỉ đổi tên field) |
| `OQ-032` | `IF-022` (Cart&Order ↔ Seller Management) — `SAD §4.1` liệt kê cả `CMP-04` và `CMP-05` đều có `IF-022` ở cột "Interface ra" — chiều gọi thật là gì? | Tech Lead | 2026-09-15 | §3.7, §5 mục 1 (denormalize `sellerName`); rà lại `SAD §4.1` ở lần cập nhật kế tiếp | Nếu chiều thật là Seller Management → Cart&Order (VD đẩy sự kiện đổi tên/khoá seller): cần thiết kế lại theo hướng event-driven thay vì REST đồng bộ, ảnh hưởng cách cập nhật `sellerName` snapshot |
| `OQ-033` | Có chấp nhận denormalize `sellerName` snapshot vào `CartItem` (tương tự `unit_price_snapshot`) để tránh gọi `IF-022` runtime mỗi lần `GET /v1/cart` không? | Tech Lead + DBA | 2026-09-15 | `DAT` (hoạt động 5, ownership trường denormalize); `QAS-003` (ngân sách latency) | Nếu từ chối: `GET /v1/cart` phải gọi `IF-022` runtime hoặc thiết kế cache riêng cho `sellerName` — cần đo lại `QAS-003`, rủi ro không đạt ngân sách ≤500ms khi giỏ có nhiều seller |
**Nhắc:** cộng với các `OQ` còn mở ảnh hưởng trực tiếp `ICD` — `OQ-007` (đại diện Pháp chế, chặn
whitelist PII trong `shippingAddressSnapshot` §4), `OQ-012` (thời điểm `POC-01`, chặn `Accepted`
của `ADR-005` mà `IF-009` phụ thuộc), `OQ-014/015/016` (biểu phí + nhà cung cấp đối tác ngoài,
chặn hoàn thiện §2.1), `OQ-018/019` (ngưỡng `QAS-003/004`), `OQ-021` (tần suất đối soát `QAS-014`),
`OQ-023` (rate limit Guest) — xem sổ đầy đủ `00-index/OQ_e-commerce.md`. Từ sổ BA:
`OQ-029/030/033/034(BA)` cần BA cập nhật theo hành động ở §5; `OQ-032(BA)` đề nghị đóng.
---
## Tự chấm
### ① Bảng tự chấm Gate AG2 *(`workflow.md §2` — chỉ dòng liên quan tới `ICD`, tự chấm sớm)*
| # | Tiêu chí | ☐/✅ | Ghi chú |
|---|---|---|---|
| — | `ASR`/`QAS`/`SAD` | ✅ *(đã đạt ở các hoạt động trước)* | Không lặp lại |
| — | `ADR-nnn` tồn tại cho mọi quyết định đạt ngưỡng radar | 🟡 Một phần | 12 `ADR` đã viết (hoạt động `adr` trước `icd` theo thứ tự chạy thực tế của dự án này — xem `DEC-17`); `ICD` không tạo `ADR` mới (quy ước §1 mục 1-8 chỉ radar ~3, dưới ngưỡng) |
| 1 | `ICD` — mọi interface ra khỏi hệ thống có contract, owner, versioning policy, chế độ sync/async | 🟡 Một phần | 17/17 `IF-nn` truy được từ `SAD` đã có đủ 2 cột owner/versioning/sync-async (§2); nhưng 5/17 giao thức "chưa rõ chiều/ranh giới" (`IF-021`, `IF-022`) hoặc "chưa có tài liệu đối tác" (`IF-007/008/018/019`, `IF-020` chưa chọn nhà cung cấp) — đã ghi `OQ` cho từng trường hợp, không bỏ sót im lặng |
| — | `DAT`/`SEC`/`INF`/`FAIL` | ☐ | Chưa tới — hoạt động 5-8 |
| — | `sa-conformance` báo coverage `ASR → ADR/CMP` ≥ 100% | *(không đổi bởi `ICD`)* | `ICD` không tạo `CMP`/`ASR` mới |
**Kết luận tự chấm (riêng phần `ICD`):** Hoạt động `icd` hoàn tất đúng phạm vi được giao — 17/17
`IF-nn` truy được từ `SAD` có dòng catalog đầy đủ 9 cột; 4 endpoint Cart & Order (`GET`/`PATCH`/
`DELETE /v1/cart*`, `POST /v1/checkout`) có chi tiết đầy đủ và đã xác nhận/điều chỉnh/đánh dấu chờ
OQ cho toàn bộ `API_US002-003_v1.0.md` của BA. Không tạo giao ước nào không có nguồn. Chênh lệch
"22 vs 17" của `SAD` được ghi nhận minh bạch, không lấp liếm. AG2 còn xa vì `DAT`/`SEC`/`INF`/
`FAIL` (hoạt động 5-8) chưa chạy và không `ADR` nào `Accepted`.
### ② Checklist D1–D12 *(`design-rules.md`)*
| # | Mục | ☐/✅ | Ghi chú |
|---|---|---|---|
| D1 | Một ADR một quyết định | N/A | `ICD` không viết `ADR` mới ở lượt này (radar §1 mục 1-8 dưới ngưỡng) |
| D2 | Không NFR định tính | N/A | `ICD` không phải `QAS`; mọi ngân sách latency tham chiếu đúng `QAS-002/003/004/010` đã lượng hoá, không tự đặt số mới |
| D3 | Nêu phương án bị loại + lý do | 🟡 Một phần | Các điều chỉnh ở §5 (VD denormalize `sellerName`) có nêu lý do gắn `QAS-003`, nhưng không đầy đủ bảng "phương án đã cân nhắc" kiểu `ADR` (không bắt buộc vì đây là `ICD`, không phải `ADR`) |
| D4 | Sơ đồ khai báo mức + legend | N/A | `ICD` không có sơ đồ mới (chỉ bảng + JSON ví dụ) |
| D5 | Interface có chủ/contract | ✅ *(với ghi chú)* | 17/17 có cột "ai sở hữu"/"contract ở đâu"/"versioning" — 5 dòng đối tác ngoài + `IF-020` ghi rõ "chưa có contract vì chưa có sandbox/nhà cung cấp", không để trống im lặng |
| D6 | Phụ thuộc ngoài process có timeout/retry | 🟡 Một phần | Timeout đề xuất kế thừa (`CTX §4.2`) đã ghi cho từng interface đối tác ngoài; retry/circuit breaker/backoff đầy đủ theo 4 câu hỏi `D6` vẫn thuộc hoạt động `fail` (chưa chạy) — `ICD` chỉ tham chiếu ứng viên `FAIL` |
| D7 | Một chủ sở hữu dữ liệu | N/A | Thuộc `DAT` (hoạt động 5); `ICD` chỉ nhắc `sellerName` denormalize cần `DAT` xác nhận ownership |
| D8 | Ràng buộc có FIT hoặc nhãn khuyến nghị | N/A | Chưa tới `AGD`/`FIT` (GĐ3) |
| D9 | Con số hạ tầng quy ra tiền + nguồn | N/A | `ICD` không thêm con số hạ tầng mới |
| D10 | Không quyết định thay người có thẩm quyền | ✅ | Mục "chờ OQ" (`OQ-030(BA)` kiểm tra tồn kho) không tự quyết; mọi đề xuất mới (denormalize, event schema, request `IF-006`) đều gắn `OQ` kèm hệ quả phương án ngược, không âm thầm coi là quyết định cuối |
| D11 | Có mục "Ngoài phạm vi" + `ASM-nn` | ✅ | §7 đầy đủ — `ASM-24…26` mới |
| D12 | Câu quy định thẩm quyền sơ đồ vs văn bản | ✅ | Có ngay sau Change Log |
### ③ Bảng đối chiếu với bộ BA
Xem §5 (bảng đầy đủ endpoint × xác nhận/điều chỉnh/chờ OQ) — đây **là** bảng đối chiếu `API`↔`ICD`
theo yêu cầu `SKILL.md` mục "Trước khi kết thúc". Tóm tắt: 6/9 dòng ✅ khớp hoàn toàn, 3/9 điều
chỉnh (có lý do rõ ràng gắn `QAS-003`/`OQ-029`/`ADR-003`), 1/9 chờ quyết định PO/Tech Lead
(`OQ-030(BA)`, không phải kiến trúc). Không có endpoint nào trong `API` thiếu `IF-nnn` tương ứng.
### ④ Danh sách `OQ` mở kèm người phải trả lời, chặn gì
Xem §8 (`OQ-028…033` mới) cộng các `OQ` còn mở liên quan trực tiếp — `OQ-007` (PII), `OQ-012`
(`POC-01`, chặn `Accepted` của `ADR-005`), `OQ-014/015/016` (đối tác ngoài), `OQ-018/019/021/023`
(ngưỡng `QAS`) — sổ đầy đủ `00-index/OQ_e-commerce.md`. Từ sổ BA cần theo dõi: `OQ-029/030/033/
034(BA)` (hành động ở §5), `OQ-032(BA)` (đề nghị đóng, tham chiếu `ADR-007`/`IF-002`).
**Nhắc:** AG2 cần **Tech Lead + Security + Ops/SRE ký** — còn xa vì `DAT`/`SEC`/`INF`/`FAIL`
(hoạt động 5-8) chưa chạy. Hoạt động kế tiếp có thể chạy song song, dùng `IF-nnn` ở đây làm đầu
vào: `dat` (bảng "Dữ liệu sở hữu" đã có ở `SAD §4.1`, cộng `sellerName` denormalize mới nêu ở
`ICD §3.7`/`OQ-033`), `sec` (mô hình authn/authz đã chi tiết hoá thêm ở `ICD §1`/§3.1), `inf`,
`fail` (dùng bảng "Đầu kia hỏng thì sao"/"Ứng viên FAIL" ở `ICD §2`).