Skip to content

SNS & outbox pattern

SNS does not implement or replace the outbox pattern, though it is often the transport an outbox publishes to.

  • Keeping a database transaction and the message that announces it consistent with each other
  • Delivering each logical event exactly once, from the application’s point of view
  • Preserving ordering where it matters
  • Recovering after a crash between the write and the publish
  • At-least-once delivery to subscribers
  • No participation in a database transaction
  • No relationship between the message and the state of the database
  • No way to unpublish a message once it has been accepted

The problem the outbox solves is atomicity between a database change and the message that announces it. SNS offers no transactional guarantee with a database, so this is not atomic:

// Not atomic
db.save(order);
sns.publish(orderCreatedEvent); // what if this fails?

If the publish fails, the order exists and nobody was told. If the process dies after the publish but before the transaction commits, subscribers act on an order that does not exist.

The write and the event go into the same transaction; a separate process publishes what the transaction committed:

// Write the event in the same transaction as the state change
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
outboxRepository.save(new OutboxEvent("OrderCreated", order));
}
// A separate process or Lambda drains the outbox
public void processOutbox() {
List<OutboxEvent> events = outboxRepository.findUnprocessed();
for (OutboxEvent event : events) {
sns.publish(event.getTopic(), event.getPayload());
outboxRepository.markAsProcessed(event.getId());
}
}

Subscribers must still be idempotent: the publisher can crash between publishing and marking the row processed, so an event can be published twice.

  • DynamoDB Streams or RDS event notifications, which turn committed changes into a feed without an explicit outbox table
  • Change data capture from the database log
  • Event sourcing, where the event is the state
  • EventBridge as the transport, with a durable source such as a stream behind it
  • Eventual consistency is acceptable and a lost event is recoverable by other means
  • Consumers already tolerate duplicates
  • The event is advisory — a notification rather than a fact other systems depend on
  • The workflow is simple enough that a missed message is visible and cheap to replay

The deciding questions are how strictly data consistency is required, what delivery guarantee consumers assume, how the system recovers after a failure, and whether ordering matters. Where any of those is critical, an outbox or an equivalent mechanism is still needed, whatever the transport.