Atomic local write
Business state and event record commit together.
A common microservice bug appears when a service updates its database successfully but fails to publish the event that other services depend on. The Outbox pattern reduces that dual-write risk by recording the event inside the same local transaction as the business state, then publishing it asynchronously.
If ordering stores a new order but fails to emit <code>OrderPlaced</code>, downstream inventory, payment, and notification services may never react. The Outbox pattern protects the causal link between state change and event publication without pretending distributed transactions are available everywhere.
The service writes business state and an outbox record atomically in one local transaction. A publisher process later reads unsent outbox rows and delivers them to the broker, marking them as published. This does not remove all complexity, but it dramatically reduces the chance of silent event loss from dual writes.
The pattern works especially well with idempotent consumers and clear event versioning. It is one of the most pragmatic building blocks in event-driven microservices because it acknowledges the reality that reliable publication is a persistence problem as much as a messaging problem.
Business state and event record commit together.
A separate publisher drains the outbox reliably.
Consumers should tolerate duplicates because reliable delivery often implies at-least-once behavior.
The order transaction stores both the domain change and a pending event record atomically.
A background publisher turns outbox records into broker messages.
Downstream services handle duplicates safely and track processed message identities where needed.
Operational metrics should track outbox backlog, publish failures, and retry health.
package org.javaomnibus.orders.persistence;
public final class OrderTransactionalWriter {
private final OrderRepository orderRepository;
private final OutboxRepository outboxRepository;
public void save(Order order) {
orderRepository.save(order);
outboxRepository.save(new OutboxMessage(
"OrderPlaced",
order.id().value(),
"{\"orderId\":\"" + order.id().value() + "\"}"
));
}
}