Ma trận hành vi khi kết hợp kết quả business × audit, cho thao tác Provisioning (Partner/Tenant/Key) và Booking.
Bất biến nền: business state và audit cùng commit hoặc cùng rollback. Kiểm chứng:src/__tests__/audit-transaction.test.ts.
| # | Business | Audit | Hành vi mong đợi | Kết quả bền vững | Kiểm chứng |
|---|---|---|---|---|---|
| 1 | success | success | Commit nguyên tử | state + đúng 1 audit | createPartner success … |
| 2 | success | fail | Rollback toàn bộ (fail-closed) | 0 state, 0 audit (không mồ côi) | createPartner + audit lỗi → rollback, suspend/issue/rotate/provision/suspendTenant + audit lỗi |
| 3 | fail | (không chạy) | Guard/nghiệp vụ ném trước khi audit | 0 state, 0 audit | transition không hợp lệ → không state/không audit |
| 4 | success | success rồi tx-commit lỗi | Cả hai rollback (cùng tx) | 0 state, 0 audit | (đảm bảo bởi $transaction — commit là nguyên tử) |
Trường "business fail + audit success" không thể xảy ra: audit chỉ được append sau state change trong cùng tx;
nếu business fail (ném) thì tx rollback trước khi commit ⇒ audit cũng mất. Không có đường nào audit tồn tại độc lập.
| Tình huống | Hành vi | Ghi chú |
|---|---|---|
| Retry sau khi thao tác thất bại (case 2/3) | An toàn: lần fail để lại 0 state, 0 audit ⇒ retry như lần đầu | Không dọn dẹp thủ công |
| Retry sau khi thao tác thành công (case 1) | Tạo thao tác mới (vd createPartner lần 2 = partner khác; issueKey lần 2 = key khác) | Đây là idempotency mức nghiệp vụ, ngoài phạm vi Beta-6. Thao tác lifecycle (activate/suspend…) lần 2 sẽ fail guard (transition sai) ⇒ case 3 |
| Retry một transition đã ở trạng thái đích | TENANT/PARTNER_INVALID_TRANSITION (exit 2), 0 state/0 audit mới | Guard chặn; không sinh audit trùng |
Không sinh audit trùng: mỗi thao tác thành công sinh đúng 1 audit (case 1). Một thao tác rollback để lại 0 audit (case 2).
Retry một transition đã hoàn tất bị guard chặn (case 3) ⇒ không có audit thứ hai.
| Business (reservation) | Audit | Hành vi | Kiểm chứng |
|---|---|---|---|
| success | success | reservation + reservation.created cùng commit | commit: createReservation success → audit … |
| fail (vd cross-tenant) | (không chạy) | Rollback: KHÔNG reservation, KHÔNG audit | rollback nghiệp vụ: booking thất bại → KHÔNG audit |
| success | append lỗi (DB) | Rollback trong cùng tx ⇒ KHÔNG reservation | đảm bảo bởi runInTenantScope ($transaction) |
Edge đã biết (Beta-7): nếu AuditEvent.record trả ok:false (input không hợp lệ), sink Booking hiện bỏ qua (không rollback). Không phát sinh với reservation.* (input luôn hợp lệ). Đề xuất fail-closed hoá sink này ở Beta-7 để đồng nhất với Provisioning.