Vấn đề dual-write: một service vừa phải ghi DB vừa phải publish event lên message broker (Kafka, RabbitMQ) — vd lưu đơn hàng rồi phát OrderCreated. Hai hệ thống này không tham gia chung một transaction được, nên crash giữa hai bước gây bất nhất: đơn đã lưu nhưng event không phát (consumer không bao giờ biết), hoặc event phát rồi mà DB rollback (consumer xử lý đơn không tồn tại). Đảo thứ tự hai bước chỉ đổi kiểu lỗi, không hết lỗi.
Transactional outbox giải quyết bằng cách đưa cả hai thao tác về một local transaction duy nhất:
- Ghi dữ liệu nghiệp vụ và insert event vào bảng outbox trong cùng transaction → atomic: hoặc cả hai có, hoặc không gì cả.
- Một tiến trình riêng (message relay) đọc bảng outbox và publish lên broker, xong đánh dấu/xóa. Hai cách hiện thực: polling bảng outbox định kỳ, hoặc CDC (change data capture — vd Debezium đọc WAL) cho độ trễ thấp hơn.
Đánh đổi: relay có thể publish trùng khi retry → đảm bảo chỉ là at-least-once, consumer phải idempotent. Đổi lại: không cần distributed transaction/2PC, event được bảo đảm phát ra đúng theo những gì đã commit.