Outboxeasy0-2 years

A checkout method saves the order row, then calls `kafka.send("orders", new OrderPlaced(...))` right after, inside the same `@Transactional` method. What's wrong with this, and what does the transactional outbox actually fix?

Two systems — the database and Kafka — with no transaction spanning both, is a dual write, and two of its four outcomes are wrong. The commit can succeed after the send already failed (the network blinked, the broker was rebalancing): the order exists with no event, so inventory never hears about it and the confirmation email never goes out. Or the send can succeed and the commit then fail: Kafka now has an event for an order that doesn't exist. Retrying the send doesn't fix it, because the process that was supposed to retry might be gone by the time it matters, and putting the send inside the transaction doesn't fix it either, because Kafka isn't a database participant that can join a rollback. The outbox fixes it by turning two writes into one: instead of calling Kafka, the code writes the event as a row in an outbox table, in the exact same transaction as the order. Now there's one commit, and it contains both or neither — a separate relay process reads that table afterward and actually publishes to Kafka.

The lesson behind it →
More on Outbox