SNS & outbox pattern
SNS does not implement or replace the outbox pattern, though it is often the transport an outbox publishes to.
What the outbox pattern is for
Section titled “What the outbox pattern is for”- 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
What SNS provides
Section titled “What SNS provides”- 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
Why that leaves a gap
Section titled “Why that leaves a gap”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 atomicdb.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.
Using an outbox with SNS
Section titled “Using an outbox with SNS”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@Transactionalpublic void createOrder(Order order) { orderRepository.save(order); outboxRepository.save(new OutboxEvent("OrderCreated", order));}
// A separate process or Lambda drains the outboxpublic 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.
Alternatives
Section titled “Alternatives”- 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
When an outbox is not needed
Section titled “When an outbox is not needed”- 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.