Command Palette

Search for a command to run...

PHASE 11Intermediate ~7 min· topic 13 of 15

Topic 11.14

Outbox Pattern

In one line

Guarantee the DB write and the event publish happen together — via a table that IS the queue.

0/15 · 0%

Think of it like this

Writing both 'pay the vendor' AND 'send the receipt' on the SAME sticky note before doing either, so you never end up having paid without sending a receipt (or vice versa) even if you get interrupted halfway.

Key ideas

  1. 01

    Problem: 'commit the order, then publish OrderPlaced' — crash between = lost event = no notifications.

  2. 02

    Outbox: write the event row into an 'outbox' table in the SAME transaction as the business write.

  3. 03

    A relay (polling job or CDC like Debezium/Debezium + Kafka) reads the outbox and publishes to the topic.

  4. 04

    Ordering: relay publishes in id/created order; consumers get roughly-ordered events.

  5. 05

    Failure handling: relay publishes with at-least-once + consumer idempotency — duplicates guaranteed impossible-ish, processed safely.

  6. 06

    Interview: 'outbox makes event publishing transactional — order + outbox row commit atomically'.

Java / Spring map

  • →

    @Transactional(order + outbox) + a scheduler publishing pending rows; Debezium as the CDC alternative.

Code & diagrams

write once, publish reliablydiagram
Rendering diagram…
OutboxPattern.javajava

The atomic write + the relay.

@Service
public class OrderService {
  private final OrderRepo orders;
  private final OutboxRepo outbox;

  @Transactional                                  // ONE transaction
  public void place(Order o) {
    orders.save(o);
    outbox.save(new OutboxEvent(                        // event row in the SAME tx
        "OrderPlaced", o.id(), o.toJson(), "PENDING"));
    // crash after commit? both are committed or neither → no lost event
  }
}

// relay — scheduled, publishes pending rows:
@Service
public class OutboxPublisher {
  private final OutboxRepo outbox;
  private final KafkaTemplate<String, String> kafka;

  @Scheduled(fixedDelay = 200)                    // or Debezium CDC instead
  public void publishPending() {
    for (OutboxEvent e : outbox.findTop100ByStatus("PENDING")) {
      kafka.send("order-events", e.aggregateId(), e.payload());
      e.markPublished();
    }
  }
}

Explain without notes

01

Why does the relay + at-least-once + idempotent consumers close the exactly-once bridge?

Practice

01

Add outbox to the payment flow and describe the ordering guarantee + the duplicate-handling.

Trade-offs

  • ↔

    Outbox adds a table + relay machinery; the alternative (publish-before-commit) is a known hole you should refuse.

Run it in production

You've designed it. Now build, operate, and break the same idea hands-on in the DevOps courses:

Completion checklist

  • I reach for outbox the moment 'write DB then publish event' appears in a design.

Back to phase