Chọn kiểu điều phối:
- Choreography — mỗi service nghe event và tự phát event tiếp, không có bộ điều phối trung tâm. Phù hợp saga ít bước (2-4) và ranh giới ổn định. Ưu: không có điểm phụ thuộc chung, service ít biết về nhau. Nhược: không ai nắm được luồng tổng thể — muốn hiểu quy trình phải đọc code 5 service; dễ sinh phụ thuộc vòng và rất khó trả lời "đơn này đang ở bước nào".
- Orchestration — một orchestrator giữ state machine, gọi từng bước và quyết định bù trừ. Phù hợp saga nhiều bước, nhiều nhánh điều kiện. Ưu: luồng nằm ở một chỗ, trạng thái truy vấn được, dễ vận hành và dễ thêm bước. Nhược: nguy cơ orchestrator hút dần logic nghiệp vụ của các service — phải giữ nó chỉ điều phối, không ra quyết định nghiệp vụ.
Thực tế hay dùng: luồng tiền/đơn hàng nhiều bước → orchestration; các phản ứng phụ (gửi mail, cập nhật số liệu) → choreography bằng event.
Điều bắt buộc nói tới — saga không có isolation. Saga chỉ là chuỗi transaction cục bộ đã commit, nên trạng thái trung gian hiển thị ra ngoài. Hệ quả kinh điển là dirty read: đơn đã trừ kho nhưng thanh toán chưa xong, người dùng khác đã thấy hết hàng. Biện pháp đối phó:
- Semantic lock — đánh dấu trạng thái PENDING/RESERVED và các bên đọc phải hiểu nghĩa trạng thái này.
- Commutative updates — thiết kế thao tác cộng/trừ giao hoán để thứ tự không đổi kết quả.
- Reread value — đọc lại và kiểm tra chưa bị đổi trước khi ghi (optimistic check).
Khi bù trừ không thể hoàn tác: email đã gửi, tiền đã chuyển ra ngoài. Cách xử lý là bù trừ theo nghĩa nghiệp vụ, không phải rollback kỹ thuật: gửi email đính chính, tạo giao dịch hoàn tiền. Nếu có bước thật sự không đảo được, xếp nó muộn nhất có thể trong saga (pivot transaction: các bước trước có thể bù trừ, các bước sau chỉ retry cho tới khi thành công). Bù trừ cũng phải idempotent và có retry, vì bản thân nó cũng có thể lỗi.