Files
Canhchimlac 343ad8bbbc save
2026-09-15 16:02:30 +07:00

601 lines
62 KiB
Markdown
Raw Permalink 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.

# 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`).