Problem
A service saves to its database and then publishes an event. If it crashes or the broker is down between the two, the database changed but no event was sent, or the reverse. Retrying code cannot fix a lost process.
Also known as: dual write anti-pattern, two-system write inconsistency
Avoid inconsistency caused by updating a database and publishing a message as two separate, non-atomic steps.
A service saves to its database and then publishes an event. If it crashes or the broker is down between the two, the database changed but no event was sent, or the reverse. Retrying code cannot fix a lost process.
The dual writes problem appears whenever code does save(entity) followed by publish(event). Each step can fail independently, and there is no transaction coordinating them.
Swapping the order only changes the failure: lose the event, or emit an event for a change that never happened.
It is worth spotting in the wild. Typical shapes include updating a database and then a search index, saving an order and then sending an email, or writing a database row and then invalidating a cache. Each looks harmless in a happy-path test and fails only under crashes, timeouts or deploys, which makes it a persistent source of data drift.
The fix is to make one write the source of truth and derive the other. With an outbox, the event row commits atomically with the state, and a relay publishes it.
-- broken
db.save(order) # crash here...
broker.publish(evt) # ...event lost
-- fixed
BEGIN; INSERT orders ...; INSERT outbox(evt); COMMIT;
relay: read outbox -> publish -> mark sent
The two main cures differ in where capture happens. A transactional outbox captures events in application code inside the same transaction. Change data capture tails the database write-ahead log and requires no application changes, but couples consumers to table structure unless you add an outbox table anyway. Either way, the relay is at-least-once, so downstream handling must tolerate duplicates.
Medium - outbox relay or CDC
Medium - relay lag and cleanup
Medium - understanding failure windows
Commit succeeds but publish fails leaving consumers unaware
Publish succeeds but commit rolls back, emitting a phantom event
Concurrent writers apply updates to the two systems in different orders