Preview Bản nháp chờ review — title, nội dung, link có thể sửa trước khi bỏ nhãn.

await db.save(order); await bus.publish(event); Process chết giữa hai dòng → DB có đơn, không event — hoặc ngược lại tùy thứ tự. Hai hệ = không atomic.

Outbox (và inbox phía nhận) là đường chuẩn. Dual-write “cho nhanh” để lại ticket “đơn có / mail không”.

Dual-write fail modes

DB ok, publish fail     → missing event (view/job đói)
Publish ok, DB fail     → ghost event (consumer làm việc trên state không có)
Retry publish           → double event nếu consumer không idempotent

Nối missing / wrong, exactly-once thực dụng.

Timeline fail

t0  BEGIN; insert order;  — crash trước commit → không có gì (OK)
t1  COMMIT order
t2  publish event         — crash trước t2 → DB có, bus không (missing)
t2' publish ok; crash trước consumer xong → redelivery (cần idempotent)
t3  publish trước; DB fail — ghost event (consumer “làm” state ảo)

Outbox ý tưởng

BEGIN
  insert business row
  insert outbox(event)
COMMIT
— publisher poll outbox / CDC — at-least-once publish
— consumer idempotent

Vì sao vẫn dual-write

Nhanh, demo, “tạm”. Chi phí: support + job backfill + mất niềm tin event-driven. Mỗi tuần “tạm” là một tuần ticket missing event.

Một câu

Hai hệ = không atomic — outbox (hoặc tương đương), đừng tin hai dòng code.