Skip to main content
Version: 1.9.x

Messaging Integrations

Namastack Outbox provides first-class support for sending outbox events to Kafka, RabbitMQ, and AWS SNS. These modules offer ready-to-use handlers, flexible routing, and simple configuration.

Quickstart: Adding Kafka, RabbitMQ, or SNS Support​

To use the Kafka, RabbitMQ, or SNS modules, simply add the corresponding dependency to your project:

dependencies {
implementation(platform("io.namastack:namastack-outbox-bom:1.9.x"))
implementation("io.namastack:namastack-outbox-kafka")
implementation("io.namastack:namastack-outbox-rabbit")
implementation("io.namastack:namastack-outbox-sns")
}

All modules are optional and can be included as needed. They are auto-configured if the corresponding Spring integration is present on the classpath.


  • Handler: KafkaOutboxHandler automatically sends outbox events to Kafka topics.
  • Routing: Customizable via a KafkaOutboxRouting bean. Define target topic, key, headers, payload mapping, and filtering per payload type.
  • Headers: Use the headers configurer to set custom Kafka headers for each message.
  • Auto-configuration: Enabled if Spring Kafka is present.
@Configuration
class KafkaOutboxConfig {
@Bean
fun kafkaOutboxRouting() = kafkaOutboxRouting {
route(OutboxPayloadSelector.type(OrderEvent::class.java)) {
target("orders")
key { payload, _ -> (payload as OrderEvent).orderId }
headers { payload, metadata -> mapOf(
"custom-header" to "value",
"traceId" to metadata.context["traceId"])
}
mapping { payload, _ -> (payload as OrderEvent).toPublicEvent() }
filter { payload, _ -> (payload as OrderEvent).status != "CANCELLED" }
}
defaults {
target("domain-events")
}
}
}

Configuration Properties

PropertyDefaultDescription
namastack.outbox.kafka.enabledtrueEnable Kafka outbox integration
namastack.outbox.kafka.default-topicoutbox-eventsDefault Kafka topic for outbox events
namastack.outbox.kafka.enable-jsontrueEnable JSON support for Kafka outbox integration

RabbitMQ Integration​

The namastack-outbox-rabbit module provides a ready-to-use outbox handler for publishing records to RabbitMQ exchanges.

RabbitMQ needs slightly more explicit configuration than some other brokers. A synchronous call to Spring AMQP's convertAndSend(...) can fail immediately when the broker is unreachable, but without publisher confirms it does not prove that RabbitMQ accepted the specific message. The Rabbit outbox integration therefore requires correlated publisher confirms.

Dependency​

dependencies {
implementation(platform("io.namastack:namastack-outbox-bom:1.9.x"))
implementation("io.namastack:namastack-outbox-rabbit")
}

Required RabbitMQ Settings​

The Rabbit outbox publisher waits for a correlated publisher confirm before the outbox record is considered successfully handled.

spring:
rabbitmq:
publisher-confirm-type: correlated

If this setting is missing, Rabbit outbox auto-configuration fails during startup with a clear configuration error. This is intentional: without correlated publisher confirms, the outbox can only know that the message was handed to the client/channel layer, not that RabbitMQ accepted the publish.

Optional Unroutable Message Detection​

By default, Namastack Outbox treats the publish as successful when RabbitMQ confirms the message. This means that publishing to an existing exchange can complete even if no queue is currently bound for the routing key.

If your application wants unroutable messages to fail outbox processing, enable:

namastack:
outbox:
rabbit:
fail-on-unroutable: true

spring:
rabbitmq:
publisher-confirm-type: correlated
publisher-returns: true
template:
mandatory: true

When fail-on-unroutable is enabled:

  • Spring AMQP publisher returns must be enabled.
  • RabbitTemplate mandatory publishing must be enabled.
  • Returned messages are converted into RabbitOutboxSendException.
  • The outbox record remains retryable instead of being marked completed.

This mode is useful when missing bindings should be treated as deployment or routing misconfiguration. Leave it disabled if publishing to an exchange without active bindings is a valid state in your topology.

What the Rabbit Outbox Guarantees​

With correlated publisher confirms enabled, RabbitOutboxPublisher blocks until RabbitMQ confirms the publish.

The handler fails and the outbox record is retried when:

  • RabbitMQ nacks the publisher confirm.
  • No publisher confirm arrives before namastack.outbox.rabbit.publisher-confirm-timeout.
  • Spring AMQP fails while sending the message.
  • Waiting for the confirm is interrupted.
  • The message is returned and namastack.outbox.rabbit.fail-on-unroutable=true.

The handler does not guarantee that a downstream consumer has processed the message. The outbox boundary ends when RabbitMQ accepts the publish. Consumer delivery and acknowledgements remain the responsibility of RabbitMQ and the consuming applications.

Configuration Properties​

PropertyDefaultDescription
namastack.outbox.rabbit.enabledtrueEnable Rabbit outbox integration.
namastack.outbox.rabbit.default-exchangeoutbox-eventsDefault Rabbit exchange for outbox events.
namastack.outbox.rabbit.enable-jsontrueEnable Jackson JSON message conversion for RabbitTemplate.
namastack.outbox.rabbit.publisher-confirm-timeout10sMaximum time to wait for RabbitMQ publisher confirms.
namastack.outbox.rabbit.fail-on-unroutablefalseWhether returned unroutable messages should fail outbox processing. Requires spring.rabbitmq.publisher-returns=true and spring.rabbitmq.template.mandatory=true when enabled.

Routing​

Routing is configured through a RabbitOutboxRouting bean. It controls the exchange, routing key, headers, payload mapping, and filtering for each payload type.

@Configuration
class RabbitOutboxConfig {
@Bean
fun rabbitOutboxRouting() = rabbitOutboxRouting {
route(OutboxPayloadSelector.type(OrderEvent::class.java)) {
target("orders-exchange")
key { payload, _ -> (payload as OrderEvent).orderId }
headers { payload, metadata ->
mapOf(
"custom-header" to "value",
"traceId" to (metadata.context["traceId"] ?: ""),
)
}
mapping { payload, _ -> (payload as OrderEvent).toPublicEvent() }
filter { payload, _ -> (payload as OrderEvent).status != "CANCELLED" }
}
defaults {
target("domain-events")
}
}
}

Migration Note​

Applications using namastack-outbox-rabbit must configure correlated publisher confirms:

spring:
rabbitmq:
publisher-confirm-type: correlated

Earlier versions used fire-and-forget publishing. That meant a record could be marked completed even when RabbitMQ rejected the publish asynchronously, for example because the exchange did not exist. The new behavior fails fast at startup if the required confirm setting is missing.

All modules are optional and can be included as needed. They provide a fast path to production-ready messaging integration with minimal configuration.