The outbox pattern for a write and a message

Saving an order and publishing an event are two systems, and there is no transaction across them. Whichever order they happen in, a failure between them leaves the two inconsistent — an order with no event, or an event for an order that was rolled back.

BEGIN;
  INSERT INTO orders (...) VALUES (...);
  INSERT INTO outbox (topic, payload, created_at)
       VALUES ('order.placed', '{...}', NOW());
COMMIT;

-- a separate worker publishes and marks rows sent

Both writes are in one database transaction, so they succeed or fail together. The worker then publishes at-least-once — it can crash after publishing and before marking, which is why consumers still have to be idempotent. What this buys is that the event is never lost and never published for work that did not happen, which is the property the naive version cannot offer at any price.