# ADR-011 — Chính sách resilience chung khi đối tác thanh toán/vận chuyển bên ngoài lỗi (timeout/retry/fallback) *Tên file: `adr/ADR-011_chinh-sach-resilience-doi-tac-ngoai.md`* | | | |---|---| | **Status** | `Proposed` | | **Date** | 2026-09-15 | | **Người quyết** | SA + Tech Lead (tích hợp — `decision-radar.md §5`) | | **Người đề xuất** | SA (qua skill `sa-2-architecture`, hoạt động `adr`) | | **Điểm radar** | **~6/10** | | **Supersedes** | — | | **Superseded by** | — | | **Liên quan** | `ASR-013` · `CON-04` · `ARISK-03` · `ARISK-04` · `CMP-10`, `CMP-11` | > ⚠️ **ADR bất biến sau khi `Accepted`.** Muốn đổi quyết định thì viết ADR mới có > `Supersedes: ADR-011` và đổi trạng thái bản cũ thành `Superseded by`. --- ## 1. Bối cảnh Hệ thống phải tích hợp đúng 4 đối tác đã chốt: VNPay, Momo (thanh toán), GHN, GHTK (vận chuyển, có fallback chéo) — `CON-04`. Không đối tác nào có sandbox/hợp đồng thật tại thời điểm này (`CTX §4.2`). `ASR-013` yêu cầu một chính sách resilience **chung** cho cả 4 đối tác (timeout, retry, idempotent) thay vì mỗi đội tự chọn một kiểu khác nhau — đây là quyết định "thiết lập tiền lệ" theo `decision-radar.md §4` (ngoại lệ để nhỏ thành ADR): áp dụng cho mọi tích hợp đối tác ngoài tương lai, không chỉ 4 đối tác hiện tại. **Ràng buộc đang chi phối:** | Nguồn | Nội dung | |---|---| | `ASR-013` | Chính sách timeout/retry/idempotent nhất quán cho VNPay/Momo/GHN/GHTK; GHN/GHTK cần fallback chéo | | `CON-04` | 4 đối tác đã chốt danh tính, chưa có sandbox/hợp đồng thật | | `ARISK-03` | VNPay/Momo — webhook chưa xác minh khả thi gần thời gian thực | | `ARISK-04` | GHN/GHTK — cơ chế fallback chéo chưa kiểm chứng hoạt động đúng giữa 2 API khác nhau | **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 | |---|---|---| | Danh tính 4 đối tác đã chốt (không phải chờ chọn) | 🟢 Đã kiểm chứng | `CON-04` | | Timeout đề xuất VNPay/Momo 10s, GHN/GHTK 8s | 🔴 Giả định — kế thừa từ `SAD.md §3.4`, chưa kiểm chứng sandbox thật | `CTX §4.2` | | Fallback GHN↔GHTK hoạt động đúng khi chuyển đổi API giữa chừng | 🔴 Giả định | `ARISK-04`, chưa có tài liệu API/sandbox thật | ## 2. Phương án đã cân nhắc ### PA-1 — Mỗi đội tự thiết kế timeout/retry riêng cho từng đối tác (không có chính sách chung) | | | |---|---| | **Mô tả** | Không có ADR/chuẩn chung — mỗi dev tích hợp VNPay/Momo/GHN/GHTK tự quyết định timeout/retry theo kinh nghiệm cá nhân | | **Ưu** | Linh hoạt tối đa cho từng đối tác cụ thể | | **Nhược** | Không nhất quán — dev khác nhau chọn số khác nhau, khó audit/thay đổi hàng loạt khi cần; đúng bẫy đã cảnh báo ở `GUIDE.md` ("Bỏ qua đường lỗi vì sẽ xử lý lúc code") | | **Chi phí đảo ngược** | Cao — chuẩn hoá lại sau khi đã có nhiều cách làm khác nhau tốn công rà soát từng điểm tích hợp | ### PA-2 — Chính sách chung: timeout theo loại đối tác + retry có backoff + idempotency key bắt buộc, adapter riêng mỗi đối tác *(chọn)* | | | |---|---| | **Mô tả** | Một chuẩn chung áp dụng cho mọi đối tác ngoài: timeout cụ thể theo loại (thanh toán 10s, vận chuyển 8s — đề xuất, chờ kiểm chứng sandbox), retry ≤3 lần với exponential backoff + jitter, idempotency key bắt buộc cho mọi lời gọi ghi; mỗi đối tác có một adapter/anti-corruption layer riêng để cô lập thay đổi API của họ | | **Ưu** | Nhất quán, dễ audit và thay đổi hàng loạt; adapter riêng giúp thay đổi API một đối tác không ảnh hưởng đối tác khác; đặt tiền lệ cho đối tác tương lai (khớp `decision-radar.md §4`) | | **Nhược** | Cần thời gian thiết kế chuẩn chung trước khi bắt tay code từng đối tác; timeout cụ thể vẫn là đề xuất chưa kiểm chứng sandbox thật | | **Chi phí đảo ngược** | Thấp — điều chỉnh số cụ thể (timeout, số lần retry) sau khi có sandbox thật là thay đổi cấu hình, không phải viết lại kiến trúc | ### Bảng so sánh | Tiêu chí | PA-1 (Tự do từng đội) | PA-2 (Chuẩn chung + adapter) | |---|---|---| | Nhất quán, dễ audit | ❌ | ✅ | | Đặt tiền lệ cho đối tác tương lai | ❌ | ✅ | | Cô lập thay đổi API một đối tác | 🔶 Tuỳ đội | ✅ (adapter riêng) | | Effort thiết kế trước khi code | Thấp | Trung bình | ## 3. Quyết định > **Chọn PA-2 — Chính sách resilience chung (timeout theo loại đối tác, retry backoff+jitter, > idempotency key bắt buộc) + adapter riêng cho mỗi đối tác.** **Vì sao:** Đây là quyết định thiết lập tiền lệ cho mọi tích hợp đối tác ngoài hiện tại và tương lai (`decision-radar.md §4`) — không nhất quán ngay từ đầu sẽ tốn công chuẩn hoá lại sau, đúng bẫy đã cảnh báo ở `GUIDE.md`. **Phạm vi áp dụng:** VNPay, Momo, GHN, GHTK, Email/SMS Provider, và mọi đối tác ngoài tương lai. ### Điều kiện chuyển `Proposed → Accepted` | Điều kiện | Ai xác nhận | Trạng thái hiện tại | |---|---|---| | Có sandbox thật của cả 4 đối tác để kiểm chứng timeout cụ thể | Tech Lead | Chưa có (`CON-04`, `ARISK-03`/`04`) | | Kiểm chứng fallback GHN↔GHTK hoạt động đúng | Tech Lead | Chưa kiểm chứng | | Không bắt buộc POC theo `decision-radar.md §6` (không phải con số hiệu năng lớn) nhưng cần sandbox thật | — | Chờ sandbox | ## 4. Phương án bị loại và lý do | Phương án | Loại vì | Gắn với | Điều kiện nào thì xét lại | |---|---|---|---| | PA-1 — Mỗi đội tự thiết kế riêng | Không nhất quán, khó audit/thay đổi hàng loạt; vi phạm nguyên tắc "đường lỗi không thể xử lý lúc code" | `GUIDE.md` "Bẫy thường gặp" | Không xét lại — đây là anti-pattern đã có cảnh báo rõ trong bộ SA | ## 5. Hệ quả **Hệ quả tích cực** - Nhất quán, dễ audit và thay đổi hàng loạt khi cần đổi chính sách retry/timeout - Adapter riêng cô lập thay đổi API một đối tác, không lan sang đối tác khác **Hệ quả tiêu cực phải sống chung** - Cần thời gian thiết kế chuẩn chung trước khi từng đội bắt tay code — có thể chậm hơn PA-1 ở giai đoạn đầu - Timeout cụ thể (10s/8s) vẫn là đề xuất chưa kiểm chứng, có thể cần điều chỉnh khi có sandbox **Cái quyết định này khoá lại** | Muốn đổi về sau thì | Tốn | |---|---| | Bỏ chuẩn chung, để từng đối tác tự do sau khi đã có nhiều adapter theo chuẩn | Không tốn nhiều nếu chỉ đổi số cụ thể; tốn nhiều nếu đổi cấu trúc adapter | **Việc phát sinh** | Việc | Chủ | Hạn | Ghi ở đâu | |---|---|---|---| | Đàm phán sandbox 4 đối tác | Tech Lead + PM | Trước GĐ2 `ICD` hoàn tất | `ARISK-03`, `ARISK-04` | | Thiết kế chi tiết từng phụ thuộc (4 câu hỏi D6) | SA (hoạt động `fail`) | GĐ2 tiếp theo | `FAIL_e-commerce` | | Viết integration test cho fallback GHN↔GHTK | Tech Lead | GĐ2/GĐ3 | `FIT` (ứng viên) | ## 6. Cách kiểm chứng quyết định này được tuân thủ | Cách kiểm | Công cụ | Chạy ở đâu | `FIT` | |---|---|---|---| | Mọi lời gọi ghi tới đối tác ngoài có idempotency key | Code review/lint + integration test | CI | `FIT-14` (ứng viên) | | Retry có backoff+jitter, không retry storm khi đối tác hồi phục | Integration test giả lập đối tác chậm/hỏng | CI + staging | `FIT-15` (ứng viên) | | Fallback GHN→GHTK kích hoạt đúng khi GHN lỗi | Integration test riêng cho luồng fallback | Staging, cần sandbox thật | — | ## 7. Điều kiện xét lại | Dấu hiệu | Ngưỡng | Ai theo dõi | |---|---|---| | Sandbox thật cho thấy timeout đề xuất sai lệch nhiều | Timeout thật khác đề xuất > 50% | Tech Lead | | Fallback GHN↔GHTK thất bại trong kiểm thử/production | Bất kỳ lần nào | Tech Lead/SRE | ## 8. Tham chiếu - POC: Không bắt buộc — cần sandbox thật để kiểm chứng đầy đủ - Bài đo: `FIT-14`, `FIT-15` (ứng viên GĐ3) - Tài liệu ngoài: `CTX_e-commerce_v1.0.md §4.2` (trạng thái đối tác), `ARISK_e-commerce_v1.0.md §3 ARISK-03/04` - Thảo luận: `ASR_e-commerce_v1.0.md §B2 ASR-013` ## 9. Review log (không đổi Status) | Ngày | Người review | Vai trò | Quyết định | Lý do chưa chuyển `Accepted` | |---|---|---|---|---| | 2026-09-15 | Điều phối dự án (thay mặt Tech Lead, chế độ chạy thử) | Tech Lead (ký thay, ngoại lệ `DEC-01`) | `Reviewed (Proposed giữ nguyên)` — nội dung đủ làm cơ sở thiết kế tiếp (`icd`/`dat`/`sec`/`inf`/`fail`) | Chưa có chữ ký thật Tech Lead; cần chạy thử trên sandbox đối tác ngoài trước khi chốt | > Đây là ghi nhận **review nội dung**, không phải "sign" theo nghĩa gate: `Status` giữ nguyên > `Proposed`. Xem `00-index/ADL_e-commerce.md` (Change Log — Review 2026-09-15) và `ADL §8` > "Việc phải làm" cho điều kiện chuyển `Accepted`. Confidence tổng thể của lượt review: 🔴 — > `AG2` chưa ký.