31 KiB
ADR-013 — Bất đồng bộ hoá bước khởi tạo thanh toán trong checkout
Tên file: adr/ADR-013_bat-dong-bo-hoa-khoi-tao-thanh-toan.md
| Status | Proposed |
| Date | 2026-09-15 |
| Người quyết | Tech Lead + PO (chưa ký thật — xem điều kiện Accepted ở §3/§7) |
| Người đề xuất | SA (qua skill sa-2-architecture, hoạt động adr) |
| Điểm radar | 7/10 (chấm theo decision-radar.md §2: Chi phí đảo ngược=2 (>4 tuần — đã lộ ra FE, đổi lại phải thiết kế lại màn hình chờ/polling và state machine) · Bán kính ảnh hưởng=2 (Cart&Order, Payment Service, FE, BA/SRS US-004, ICD) · Chạm QAS Must=2 (trực tiếp QAS-002) · Ràng buộc dài hạn=1 (ràng trong phạm vi thiết kế hiện tại, xét lại được khi có sandbox thật) · Tranh cãi=0 (lần đầu đề xuất, chưa tranh luận nhiều vòng) → 5–7 ⇒ ADR bắt buộc, dưới ngưỡng 8 nên không bắt buộc POC trước khi Accepted, nhưng vẫn cần bài đo xác nhận theo §6) |
| Supersedes | — (không supersede ADR nào — luồng đồng bộ trước đó là một phần SAD §6.1 "duyệt từng phần" dưới ngoại lệ gate, chưa từng là một ADR riêng, nên không có ADR nguồn để supersede; SAD §6.1 được sửa trực tiếp kèm dẫn chiếu ADR-013, xem SAD_e-commerce_v1.0.md v1.3 Change Log) |
| Superseded by | — |
| Liên quan | ASR-004 · QAS-002 · DRV-02 · CON-04 · ARISK-03 · ADR-004 (idempotency/đối soát webhook) · ADR-005 (SQS FIFO/EventBridge) · ADR-011 (chính sách resilience đối tác ngoài) · OQ-034 (đã chọn hướng B, DEC-22) |
⚠️ ADR bất biến sau khi
Accepted. Muốn đổi quyết định thì viết ADR mới cóSupersedes: ADR-013và đổi trạng thái bản cũ thànhSuperseded by. Không sửa nội dung ADR đãAccepted, không xoá ADR đãRejected.
0. Ghi chú Preflight & ngoại lệ gate — bắt buộc đọc trước
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 từ trước (DEC-12/14/16); hoạt động 4
(ICD) và 8 (FAIL) đã chạy (DEC-18…22). Điều kiện đầu vào cho việc viết ADR-013 (kết luận
của FAIL §1.2, đã được điều phối dự án chọn Lựa chọn B qua OQ-034/DEC-22) đã đủ. 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-23 (SA) — Viết
ADR-013và sửa nhất quán tối thiểuSAD §6.1/§8+FAIL §1.2cho e-commerce trong khi AG1 chưa ký thật và AG2 chưa tới hạn — tiếp nốiDEC-01…22. Quyết định: Hiện thực hoá Lựa chọn B đã chọn ởOQ-034(DEC-22) thành mộtADRchính thức, mô tả đầy đủ luồng bất đồng bộ hoá khởi tạo thanh toán, và sửaSAD §6.1(sequence diagram checkout) +SAD §8(bảng ADR) +FAIL §1.2(ngân sách timeout cộng dồn) cho khớp với quyết định này. Không sửa nội dung khác củaSAD/FAILngoài phạm vi được giao. GiữConfidence 🔴cho tới khi: (a) AG1/AG2 ký thật, (b) Tech Lead + PO thật xác nhậnADR-013(không chỉ điều phối dự án ký thay), (c) BA cập nhậtSRS/ACcho US-004 Checkout theo trạng thái mới, (d)ICDlượt sau chốt endpoint/cơ chế FE lấyredirectUrl(polling hay SSE —OQ-041mới). Người quyết: Điều phối dự án (đại diện PO, dự án chạy thử) — tiếp tục hoạt động dưới ngoại lệ; bản thân nội dungADR-013vẫn cần Tech Lead + PO thật ký trước khiAccepted. Radar (ước lượng cho việc tiếp tục dưới ngoại lệ, khác điểm radar 7/10 của chínhADR-013ở trên): ~4 — chi phí đảo ngược thấp (đây là việc ghi tài liệu, không phải thi công) · bán kính ảnh hưởng:SAD/FAILđã tồn tại, chỉ sửa đúng 2 mục theo yêu cầu người duyệt · chạmQAS-002Must gián tiếp (đang sửa lại cách đạt nó, không phải hạ chuẩn) · không ràng buộc dài hạn tự thân (khácADR-013— bản thân việc chạy dưới ngoại lệ thì sửa được) · không tranh cãi mới, tiếp nối tiền lệDEC-11…22→ ghiDEC-nn, không cầnADRriêng cho việc chạy dưới ngoại lệ. Hệ quả nếu không chấp nhận ngoại lệ:OQ-034tiếp tục treo ở trạng thái "đã chọn hướng B nhưng chưa có ADR" — Dev không có tài liệu để thi công lại luồng checkout,SAD §6.1vàFAIL §1.2tiếp tục mô tả một luồng đã biết là vượt ngân sáchQAS-002(~7%) mà không có phương án sửa chính thức.
1. Bối cảnh
FAIL_e-commerce_v1.0.md §1.2 (bảng ngân sách timeout cộng dồn đường găng checkout) phát hiện:
ngay cả sau khi siết timeout VNPay/Momo xuống 1.500ms (thay 10s kế thừa CTX §4.2), tổng thời
gian phản hồi POST /v1/checkout ở kịch bản worst-case cộng dồn đạt 3.220ms — vượt QAS-002
(≤3.000ms, mức Must) khoảng 7%. Nguyên nhân gốc: một mình bước "Payment Service gọi VNPay/Momo"
(bước 8 trong bảng ngân sách) đã chiếm phần lớn ngân sách còn lại, trong khi ta không kiểm soát
được SLA thật của đối tác ngoài (CON-04 — SLA VNPay/Momo/GHN/GHTK chưa xác nhận; ARISK-03 —
chưa có sandbox thật để đo). Đây đúng là tình huống vi phạm D6: "timeout tầng ngoài phải lớn
hơn timeout tầng trong" — ở luồng đồng bộ hiện tại, timeout tầng trong (đối tác thanh toán) đang
lớn hơn timeout tầng ngoài (toàn bộ checkout).
DRV-02 (giỏ hàng ≥2 seller có nguy cơ tỷ lệ bỏ giỏ cao hơn ở bước checkout) là driver kinh doanh
đứng sau — nếu checkout thường xuyên chậm hoặc timeout do đối tác thanh toán, rủi ro bỏ giỏ càng
tăng đúng ở bước cuối cùng, nơi thiệt hại kinh doanh là lớn nhất (khách đã hoàn tất chọn hàng và
tách đơn theo seller).
Ràng buộc đang chi phối:
| Nguồn | Nội dung |
|---|---|
QAS-002 |
Latency checkout POST /v1/checkout: p95 ≤ 3 giây, mức Must — không đạt thì không được go-live theo định nghĩa Must của sa-2-architecture/SKILL.md |
ASR-004 |
Webhook xác nhận thanh toán phải idempotent + có job đối soát bù — ép kiến trúc chấp nhận eventual consistency có giới hạn thời gian cho trạng thái thanh toán; ADR-013 mở rộng đúng tinh thần này sang cả bước khởi tạo thanh toán, không chỉ bước xác nhận |
CON-04 |
SLA thật của đối tác thanh toán/vận chuyển ngoài chưa được xác nhận bằng hợp đồng |
ARISK-03 |
Chưa có sandbox thật VNPay/Momo để đo latency init thật — mọi con số timeout hiện tại là đề xuất SA |
Cái đã biết chắc / cái còn là giả định:
| Điều | 🟢 Đã kiểm chứng / 🔴 Giả định | Bằng chứng |
|---|---|---|
| Tổng ngân sách worst-case cộng dồn của luồng đồng bộ hiện tại = 3.220ms > 3.000ms | 🟢 Đã kiểm chứng (bằng tính toán cộng dồn, không phải đo thật) | FAIL_e-commerce_v1.0.md §1.2 bảng 10 bước |
| Tách đường găng đồng bộ (bước 1–7, 10) khỏi bước khởi tạo thanh toán (bước 8) đưa tổng thời gian phản hồi xuống ≤3.000ms bất kể VNPay/Momo chậm bao lâu | 🟢 Đã kiểm chứng bằng tính toán (1.670ms + 50ms = 1.720ms, còn dư ~1.280ms so với ngân sách) | Xem bảng §2.3 dưới |
| VNPay/Momo hoàn tất init trong ≤10s ở phần lớn trường hợp khi không còn bị ép timeout ngắn | 🔴 Giả định mới — ASM-34 |
Cần sandbox thật (ARISK-03) để xác minh; nếu sai, cửa sổ chờ của FE (§2.2) cần tăng hoặc coi thất bại sớm hơn |
FE có thể triển khai polling hoặc kênh đẩy (SSE/WebSocket) để lấy redirectUrl sau khi có |
🔴 Giả định — chưa có xác nhận năng lực FE hiện tại | Cần Tech Lead FE xác nhận — xem OQ-041 |
2. Phương án đã cân nhắc
PA-A — Giữ đồng bộ, siết timeout hơn nữa
| Mô tả | Giảm timeout gọi VNPay/Momo trong nhánh đồng bộ xuống 1.200ms (thay vì 1.500ms đã đề xuất ở FAIL v1.0), đưa tổng ngân sách cộng dồn còn ~2.920ms, có margin ~80ms |
| Ưu | Không đổi cấu trúc luồng, không đổi UX FE, không cần state machine mới, không cần BA viết lại SRS US-004 |
| Nhược | Chỉ là "ép con số" chứ không giải quyết nguyên nhân gốc — ta vẫn không kiểm soát được SLA thật của VNPay/Momo (CON-04/ARISK-03 chưa có sandbox). Nếu đối tác thật cần >1.200ms để phản hồi (rất có thể, vì 10s là con số kế thừa từ hợp đồng gốc, không phải p95 thật đo được), tỷ lệ timeout/thất bại init tăng, khách phải tự bấm "Thử lại" nhiều lần — đúng lúc rủi ro bỏ giỏ hàng cao nhất (DRV-02). Margin 80ms cũng rất mỏng, một dao động nhỏ ở bước 1 (network client→ALB, hiện đề xuất 150ms, ASM-31 chưa đo thật) đã có thể lại vượt ngân sách |
| Chi phí đảo ngược | Thấp về mặt kỹ thuật (chỉ đổi số cấu hình) — nhưng rủi ro kinh doanh (bỏ giỏ hàng) không định lượng được trước và có thể đã gây thiệt hại trước khi phát hiện |
PA-B — Bất đồng bộ hoá bước khởi tạo thanh toán (chọn)
| Mô tả | Tách bước gọi VNPay/Momo ra khỏi đường găng đồng bộ của POST /v1/checkout. Order/OrderSeller được tạo và commit ở trạng thái chờ, response trả về ngay cho client trong ngân sách đồng bộ (~1.720ms, xem §2.3); việc gọi VNPay/Momo và lấy redirectUrl diễn ra sau, bất đồng bộ. FE lấy redirectUrl qua một cơ chế riêng (polling hoặc kênh đẩy — OQ-041). COD không đổi (không phụ thuộc bước này) |
| Ưu | QAS-002 được đảm bảo về mặt cấu trúc — không còn phụ thuộc vào SLA của bên thứ ba ta không kiểm soát được, đúng tinh thần D6. Hệ quả phụ tích cực: TX DB không còn phải giữ mở xuyên qua lời gọi mạng tới Payment Service (giải quyết một phần mối lo bulkhead đã nêu ở OQ-037/OQ-038 của FAIL) |
| Nhược | Đổi UX checkout đáng kể (khách không nhận redirectUrl ngay, cần chờ/poll); thêm độ phức tạp state machine cho Order; cần BA viết lại SRS/AC cho US-004; cần ICD lượt sau chốt endpoint mới; cần xử lý "Order kẹt ở trạng thái chờ" nếu bước async không bao giờ hoàn tất (tương tự khoảng trống outbox đã ghi ở FAIL §7) |
| Chi phí đảo ngược | Cao — >4 tuần nếu phải quay lại đồng bộ sau khi FE đã triển khai polling/kênh đẩy và BA đã cập nhật SRS theo trạng thái mới |
PA-C — Giữ đồng bộ, chấp nhận vượt ngân sách QAS-002
| Mô tả | Không sửa gì, chấp nhận p95 lý thuyết worst-case 3.220ms, coi đây là biên an toàn hiếm khi chạm tới trong thực tế (đa số request sẽ nhanh hơn nhiều vì không phải mọi bước đều đồng thời chạm timeout tối đa) |
| Ưu | Không tốn công sức thiết kế lại, không đổi UX, không đổi tài liệu |
| Nhược | Vi phạm trực tiếp một QAS mức Must — theo định nghĩa Must ("không đạt thì không được go-live") và tiêu chí chặn của AG2 ("còn interface/QAS nào không đạt mà không có phương án"), đây không phải lựa chọn SA có thẩm quyền tự chấp nhận (D10) — chấp nhận rủi ro vi phạm một ràng buộc chất lượng đã cam kết là quyết định của PO/Tech Lead, không phải mặc định giữ nguyên trong im lặng |
| Chi phí đảo ngược | Thấp về kỹ thuật, nhưng không phải là một quyết định — đây là không quyết định gì cả và để lại một QAS Must đã biết là không đạt trôi tới go-live |
Bảng so sánh
Tiêu chí (sinh từ QAS-002/CON-04/DRV-02) |
PA-A | PA-B | PA-C |
|---|---|---|---|
Đảm bảo QAS-002 (Must) bất kể SLA đối tác |
🟡 Không chắc chắn (margin mỏng, phụ thuộc giả định 1.200ms) | 🟢 Có (về mặt cấu trúc) | 🔴 Không |
Không phụ thuộc SLA đối tác ngoài chưa xác nhận (CON-04) |
🔴 Vẫn phụ thuộc | 🟢 Độc lập | 🔴 Vẫn phụ thuộc |
| Chi phí thay đổi FE/BA | 🟢 Không đổi | 🔴 Đổi đáng kể | 🟢 Không đổi |
Rủi ro bỏ giỏ hàng do timeout init (DRV-02) |
🟡 Có thể tăng, không định lượng được | 🟢 Loại bỏ nguyên nhân này | 🔴 Không xử lý |
Đúng thẩm quyền quyết định (D10) |
✅ SA có thể đề xuất, PO/Tech Lead chọn | ✅ SA có thể đề xuất, PO/Tech Lead chọn | ❌ Vi phạm Must trong im lặng, không phải lựa chọn hợp lệ để "không làm gì" |
3. Quyết định
Chọn PA-B — Bất đồng bộ hoá bước khởi tạo thanh toán online (VNPay/Momo) trong
POST /v1/checkout.
Vì sao: Đây là cách duy nhất trong ba phương án đảm bảo QAS-002 (Must) đạt được về mặt
cấu trúc, không phụ thuộc vào một SLA đối tác ngoài mà CON-04/ARISK-03 xác nhận là chưa
kiểm chứng được. PA-A chỉ giảm nhẹ rủi ro mà không loại bỏ nguyên nhân gốc; PA-C vi phạm trực
tiếp một ràng buộc Must mà SA không có thẩm quyền tự chấp nhận thay Tech Lead/PO (D10).
Phạm vi áp dụng: Chỉ áp dụng cho phương thức thanh toán online qua cổng bên thứ ba (VNPay,
Momo). COD không đổi — vì COD không gọi ra ngoài, xác nhận ngay trong luồng đồng bộ hiện có
(bước 7 cũ), không có nguyên nhân gây vượt ngân sách. Áp dụng cho POST /v1/checkout (US-004,
chưa có SRS/AC chính thức từ BA — xem §5 Việc phát sinh).
3.1 Mô tả luồng mới
Đường găng đồng bộ (không đổi thứ tự, chỉ dừng lại sớm hơn):
| Bước | Thành phần | Timeout đề xuất (kế thừa FAIL §1.2) |
Cộng dồn |
|---|---|---|---|
| 1 | Client → ALB | 150ms | 150ms |
| 2 | ALB → Cart & Order | 20ms | 170ms |
| 3 | Xác thực/ownership (cache hit) | 50ms | 220ms |
| 4 | Kiểm tra/giữ tồn kho (in-process) | 150ms | 370ms |
| 5 | Áp dụng coupon/điểm (IF-021) |
400ms | 770ms |
| 6 | Ghi Order/OrderSeller/OrderItem (TX) — COMMIT ngay sau bước này, không giữ TX mở chờ Payment nữa |
300ms | 1.070ms |
| 7 | (chỉ áp dụng cho COD) Payment Service xác nhận COD, đồng bộ | 600ms | 1.670ms (nhánh COD) |
| 10 | Response serialization + trả về client | 50ms | 1.720ms (nhánh COD) |
(nhánh VNPay/Momo dừng đường găng đồng bộ ngay sau bước 6 + bước 10, không đi qua bước 7 đồng bộ nữa — xem bảng dưới)
| Bước | Thành phần | Timeout | Cộng dồn (nhánh VNPay/Momo) |
|---|---|---|---|
| 1–6 | (giống bảng trên) | — | 1.070ms |
| 10 | Response serialization + trả về client — trả orderId + status: PENDING_PAYMENT_INIT, chưa có redirectUrl |
50ms | 1.120ms |
Kết luận: cả hai nhánh đều nằm sâu dưới QAS-002 (≤3.000ms) — nhánh VNPay/Momo còn dư
~1.880ms margin so với ngân sách, đủ bù đắp mọi sai lệch chưa đo thật ở bước 1/3/4/5 (ASM-27,
ASM-28, ASM-31).
Bất đồng bộ (sau khi đã trả response, không tính vào QAS-002):
stateDiagram-v2
[*] --> PENDING_PAYMENT_INIT: TX commit (bước 6), response 201 trả về (bước 10)
PENDING_PAYMENT_INIT --> PAYMENT_INIT_READY: Payment Service gọi VNPay/Momo thành công, có redirectUrl
PENDING_PAYMENT_INIT --> PAYMENT_INIT_FAILED: Hết retry / timeout / lỗi từ VNPay/Momo
PAYMENT_INIT_FAILED --> PENDING_PAYMENT_INIT: Khách chọn "thử lại" (retry init, cùng Idempotency-Key)
PAYMENT_INIT_FAILED --> COD: Khách chọn chuyển sang COD (nếu seller/sản phẩm hỗ trợ)
PAYMENT_INIT_READY --> [*]: Khách hoàn tất thanh toán trên trang VNPay/Momo (luồng webhook — ADR-004, không đổi)
Cơ chế trigger bước async: Cart & Order publish message PaymentInitRequested (message group
= seller_id, dedupe theo eventId, cùng cơ chế SQS FIFO đã có — ADR-005) ngay sau khi commit
TX (bước 6), cùng lúc với việc publish OrderPlaced đã có sẵn (không phải luồng mới hoàn
toàn, tái dùng hạ tầng message backbone hiện có). Payment Service (CMP-11) consume message này,
gọi VNPay/Momo với timeout 10s kế thừa nguyên trạng CTX §4.2 (không cần ép ngắn nữa, vì
không còn nằm trên đường găng phản hồi người dùng), cập nhật trạng thái, publish
PaymentInitReady/PaymentInitFailed.
Idempotency của bước async: Idempotency-Key gốc của POST /v1/checkout (ICD §3.3,
ADR-004) được truyền kèm message PaymentInitRequested — nếu Payment Service consume message
này nhiều lần (redelivery do visibility timeout, ASM-29), lời gọi VNPay/Momo không bị lặp lại
sau khi đã có gatewayTransactionRef (kiểm tra trạng thái hiện tại trước khi gọi lại — check-
then-act, cùng nguyên tắc đã áp dụng cho GHN/GHTK ở FAIL FM-03/04).
Timeout tổng thể của giai đoạn async (góc nhìn UX, không phải ràng buộc kiến trúc cứng): đề
xuất coi là "quá lâu, cần hành động" nếu sau 15 giây kể từ khi tạo Order mà vẫn ở
PENDING_PAYMENT_INIT (10s timeout đối tác + margin xử lý/hàng đợi) — khi đó FE nên chuyển sang
trạng thái cho phép khách chủ động thao tác (xem §3.2) thay vì chờ vô thời hạn. Đây là con số đề
xuất SA, gắn OQ-042 (thời gian giữ Order ở PENDING_PAYMENT_INIT trước khi coi là "kẹt" và
cần job dọn dẹp/hủy tự động).
3.2 Cơ chế FE lấy redirectUrl — đề xuất, chưa chốt
Hai lựa chọn kỹ thuật cho FE nhận biết khi PAYMENT_INIT_READY sẵn sàng:
| Cơ chế | Mô tả | Đánh đổi |
|---|---|---|
| Polling (đề xuất mặc định) | FE gọi lặp lại một endpoint trạng thái (đề xuất GET /v1/orders/{orderId}/payment-init-status, chưa chốt tên — ICD lượt sau) mỗi ~1 giây, tối đa ~15 giây |
Đơn giản, không cần hạ tầng mới (WebSocket/SSE), hoạt động tốt qua mọi proxy/CDN hiện có; tốn thêm request lặp lại (tải nhỏ, ngắn hạn, không đáng kể so với QAS-009) |
| SSE / WebSocket | Server đẩy sự kiện khi trạng thái đổi | Trải nghiệm mượt hơn (không có độ trễ polling interval), nhưng cần thêm thành phần hạ tầng (kết nối lâu dài, cân bằng tải phù hợp), tăng độ phức tạp vận hành mà đội chưa xác nhận có kinh nghiệm (OQ-010 vẫn mở) |
🔴 Đây là đề xuất, không phải quyết định cuối — ADR-013 chỉ chốt việc bất đồng bộ hoá,
không chốt cơ chế truyền tải cụ thể. Ghi OQ-041 (mới), chờ Tech Lead (kèm ý kiến FE) xác nhận;
hệ quả phương án ngược: chọn polling mà tải cao bất thường có thể tăng số request nhỏ không cần
thiết (giảm thiểu bằng interval hợp lý + dừng poll sau 15s hoặc khi có kết quả); chọn SSE/WebSocket
mà đội chưa có kinh nghiệm vận hành production kênh kết nối lâu dài có thể phát sinh sự cố khó
chẩn đoán đúng lúc ra mắt.
3.3 Hành vi khi khởi tạo thất bại (PAYMENT_INIT_FAILED)
Khi hết retry / timeout ở bước async (VNPay/Momo không phản hồi được trong 10s, hoặc trả lỗi):
Order chuyển PAYMENT_INIT_FAILED. FE (qua polling/kênh đẩy) nhận trạng thái này và cho khách
hai lựa chọn, không tự động rollback/huỷ Order:
- Thử lại cùng phương thức — gọi lại endpoint khởi tạo (đề xuất tái dùng cùng
Idempotency-Key,ICDlượt sau chốt contract cụ thể) → quay lạiPENDING_PAYMENT_INIT. - Chuyển sang COD (nếu seller/sản phẩm trong đơn hỗ trợ COD) hoặc chọn phương thức online
khác (VNPay ⇄ Momo) — tái sử dụng cùng
Order/OrderSellerđã tạo, chỉ đổi phương thức thanh toán, không tạoOrdermới (tránh trùng đơn, giữ đúng ownership tồn kho đã reserve ở bước 4).
🔴 Chi tiết endpoint/contract cho hai thao tác trên chưa chốt — thuộc hoạt động icd lượt
sau (xem §5 Việc phát sinh).
4. Phương án bị loại và lý do
| Phương án | Loại vì | Gắn với | Điều kiện nào thì xét lại |
|---|---|---|---|
| PA-A (siết timeout xuống 1.200ms, giữ đồng bộ) | Không loại bỏ nguyên nhân gốc (phụ thuộc SLA đối tác chưa xác nhận, CON-04); margin còn lại (~80ms) quá mỏng so với các con số chưa đo thật khác trong cùng ngân sách (ASM-27/28/31) |
CON-04, ARISK-03 |
Nếu có sandbox thật và VNPay/Momo xác nhận SLA hợp đồng ≤1.000ms p99, có thể xét lại PA-A như một tối ưu bổ sung (không nhất thiết thay thế PA-B, có thể kết hợp: PA-B đã chọn + timeout đối tác chặt hơn nếu SLA cho phép) |
PA-C (giữ đồng bộ, chấp nhận vượt QAS-002) |
Vi phạm trực tiếp một QAS mức Must mà SA không có thẩm quyền tự chấp nhận thay Tech Lead/PO (D10); không phải "không làm gì" là an toàn — đây là để một rủi ro đã biết trôi tới go-live trong im lặng |
QAS-002 (Must) |
Chỉ xét lại nếu PO/Tech Lead chính thức hạ mức QAS-002 từ Must xuống Should/Could kèm lý do nghiệp vụ — đây là quyết định PO, không phải kỹ thuật |
"Giữ đồng bộ vì đơn giản hơn cho FE" là lý do có thật (chi phí thi công thấp hơn) — nhưng không đủ để thắng một ràng buộc Must đã lượng hoá; ghi nhận ở đây để không ai đề xuất lại PA-C mà không đọc lý do đã bị loại.
5. Hệ quả
Hệ quả tích cực
QAS-002(Must) được đảm bảo về mặt cấu trúc cho mọi request VNPay/Momo — không còn phụ thuộc SLA đối tác ngoài mà ta không kiểm soát (CON-04/ARISK-03), đúng nguyên tắcD6.- Loại bỏ hoàn toàn tình huống giữ transaction DB mở xuyên qua một lời gọi mạng ra ngoài container
(TX nay commit ngay sau bước 6) — giảm trực tiếp rủi ro cạn connection pool đã nêu ở
FAIL OQ-037/OQ-038, dù không đóng hẳn haiOQđó (bulkhead vẫn cần cho các phụ thuộc khác). - Payment Service có thể dùng lại nguyên trạng timeout 10s kế thừa
CTX §4.2cho VNPay/Momo mà không cần "ép" xuống 1.500ms như đề xuất tạm ởFAIL v1.0— giảm rủi ro timeout giả (false timeout) khi đối tác chỉ chậm tạm thời.
Hệ quả tiêu cực phải sống chung
- UX checkout đổi đáng kể: khách không nhận
redirectUrlngay trong response đầu tiên, cần chờ một khoảng thời gian ngắn (kỳ vọng thường vài trăm ms tới vài giây, tối đa đề xuất 15s trước khi coi là bất thường) — cần màn hình chờ/trạng thái mới ở FE, chưa thiết kế ở lượt này. - Thêm độ phức tạp: state machine 4 trạng thái mới cho
Order(PENDING_PAYMENT_INIT→PAYMENT_INIT_READY/PAYMENT_INIT_FAILED→ …), thêm 1 loại message (PaymentInitRequested) cần DLQ/retry riêng (tương tự các message khác đã có ởADR-005). - Khoảng trống chưa xử lý: nếu message
PaymentInitRequestedbị mất hoặc Payment Service không bao giờ xử lý (sự cố),Orderkẹt vĩnh viễn ởPENDING_PAYMENT_INIT— cần job quét dọn (cùng bản chất với khoảng trống outbox đã ghi ởFAIL §7, chưa thiết kế chi tiết, thuộc hoạt độngdat/infsắp tới). GhiOQ-042.
Cái quyết định này khoá lại
| Muốn đổi về sau thì | Tốn |
|---|---|
| Quay lại luồng đồng bộ hoàn toàn (bỏ polling/kênh đẩy) | Thiết kế lại FE (bỏ màn hình chờ), viết ADR mới Supersedes: ADR-013, cập nhật lại SRS US-004 lần nữa, và chỉ khả thi nếu có sandbox thật chứng minh SLA đối tác đủ nhanh (ARISK-03 đã đóng) |
Đổi cơ chế polling ↔ SSE/WebSocket (sau khi đã chọn một trong hai ở OQ-041) |
Đổi tầng truyền tải FE↔BE, không đổi state machine Order — chi phí trung bình, không cần ADR mới (thuộc ICD, không phải quyết định kiến trúc cấu trúc) |
Việc phát sinh
| Việc | Chủ | Hạn | Ghi ở đâu |
|---|---|---|---|
Cập nhật SAD §6.1 (sequence diagram checkout) + §8 (bảng ADR) theo ADR-013 |
SA | Đã làm cùng lượt (SAD v1.3) |
SAD_e-commerce_v1.0.md |
Cập nhật FAIL §1.2 (ngân sách timeout cộng dồn, tách đường găng đồng bộ khỏi bước async) |
SA | Đã làm cùng lượt (FAIL v1.1) |
FAIL_e-commerce_v1.0.md |
| Chốt endpoint/contract: trạng thái polling, retry init, chuyển COD | SA (hoạt động icd lượt sau) |
Trước khi Dev thi công US-004 | ICD_e-commerce_v1.0.md (bổ sung IF mới) |
Viết/cập nhật SRS/AC cho US-004 Checkout với trạng thái PENDING_PAYMENT_INIT/PAYMENT_INIT_READY/PAYMENT_INIT_FAILED |
BA | Trước khi Dev thi công | ba-output/e-commerce/03-specification/SRS_US004_*.md (chưa tồn tại) |
Thiết kế job quét Order kẹt ở PENDING_PAYMENT_INIT quá hạn (OQ-042) |
SA (hoạt động dat/inf lượt sau) |
Trước AG2 | DAT_e-commerce_v1.0.md (chưa tồn tại) |
| Thêm fitness function đo p95 checkout thật ≤3s bất kể VNPay/Momo chậm | SA/QA (GĐ3) | GĐ3 | FIT-23 (mới, xem §6) |
6. Cách kiểm chứng quyết định này được tuân thủ
| Cách kiểm | Công cụ | Chạy ở đâu | FIT |
|---|---|---|---|
Đo p95 POST /v1/checkout (nhánh VNPay/Momo) ≤3.000ms ngay cả khi giả lập VNPay/Momo phản hồi chậm (chaos, ép độ trễ 9–10s ở bước async) — xác nhận response không bị ảnh hưởng |
k6 + chaos injection (giả lập độ trễ đối tác) | Staging | FIT-23 (mới, cộng thêm vào danh sách FIT-16…22 đã đề xuất ở FAIL §8) |
Xác nhận không có Order nào giữ TX DB mở quá bước 6 (kiểm tra thời lượng transaction qua log/APM) |
Kiểm tra APM/log transaction duration | Staging + production sau go-live | FIT-24 (mới) |
Xác nhận message PaymentInitRequested idempotent — gửi trùng không gọi lại VNPay/Momo khi đã có gatewayTransactionRef |
Test tích hợp: publish trùng message, xác nhận chỉ 1 lời gọi gateway | Staging | FIT-25 (mới) |
Cả ba FIT-23/24/25 là ứng viên, chưa viết thật (thuộc GĐ3 sa-3-enablement, cùng nhóm với
FIT-16…22 đã đề xuất ở FAIL §8) — không giả vờ đã kiểm chứng.
7. Điều kiện xét lại
| Dấu hiệu | Ngưỡng | Ai theo dõi |
|---|---|---|
Tỷ lệ Order rơi vào PAYMENT_INIT_FAILED sau go-live |
> 5% tổng số checkout VNPay/Momo trong 1 tuần (đề xuất, chưa xác nhận — cần baseline thật sau go-live) | Tech Lead + PO |
Thời gian trung bình từ PENDING_PAYMENT_INIT → PAYMENT_INIT_READY |
> 5 giây p95 (gần chạm ngưỡng "kẹt" 15s ở §3.1) | SRE |
| Sandbox VNPay/Momo thật cho thấy SLA p99 ổn định ≤ 1.000ms | Có báo cáo SLA chính thức từ đối tác | Tech Lead (đóng ARISK-03) |
| Phản hồi người dùng về UX chờ/poll (khảo sát/CSKH) cho thấy trải nghiệm xấu đáng kể | Ngưỡng định tính, cần PO đánh giá | PO |
Điều kiện chuyển Accepted: Tech Lead + PO thật (không phải ký thay dưới ngoại lệ
DEC-01) xác nhận nội dung §3 (mô tả luồng), đồng thời BA xác nhận đã cập nhật SRS/AC cho
US-004 theo trạng thái mới. Radar 7/10 (dưới ngưỡng 8) nên không bắt buộc POC trước Accepted,
nhưng khuyến nghị mạnh chạy FIT-23 (chaos đo p95) ít nhất một lần ở staging trước khi go-live,
vì đây là ràng buộc chất lượng mức Must.
8. Tham chiếu
- POC: (không bắt buộc — radar 7/10, dưới ngưỡng 8)
- Bài đo:
FIT-23/24/25(ứng viên, chưa chạy — xem §6) - Tài liệu ngoài:
CTX_e-commerce_v1.0.md §4.2(timeout đối tác kế thừa 10s) - Thảo luận:
FAIL_e-commerce_v1.0.md §1.2(phát hiện xung đột ngân sách),OQ-034/DEC-22(quyết định chọn Lựa chọn B, 2026-09-15, điều phối dự án thay mặt Tech Lead/PO)
9. Review log (không đổi Status)
| Ngày | Người review | Vai trò | Quyết định | Lý do chưa chuyển Accepted |
|---|---|---|---|---|
| 2026-09-15 | Điều phối dự án (thay mặt Tech Lead, chế độ chạy thử) | Tech Lead (ký thay, ngoại lệ DEC-01) |
Reviewed (Proposed giữ nguyên) — nội dung §3 (state machine PENDING_PAYMENT_INIT/PAYMENT_INIT_READY/PAYMENT_INIT_FAILED, timeout async 10s, idempotency, hành vi thất bại) đủ làm cơ sở thiết kế tiếp (ICD/DAT/FE) |
Chưa có chữ ký thật Tech Lead + PO (§7 điều kiện Accepted); BA chưa viết SRS/AC cho US-004 (OQ-034) |
Đây là ghi nhận review nội dung, không phải "sign" theo nghĩa gate:
Statusgiữ nguyênProposed. Xem00-index/ADL_e-commerce.md(Change Log — Review 2026-09-15) vàADL §8"Việc phải làm" (mục #9) cho điều kiện chuyểnAccepted. Confidence tổng thể của lượt review: 🔴 —AG2chưa ký.