Spring Modulith Integration
Spring Modulith (2.1+) integrates with Namastack Outbox as an outbox-backed event externalization solution.
This integration lets Spring Modulith applications publish externalized application events through Namastack Outbox instead of relying only on Spring Modulith's Event Publication Registry.
Why this integration exists
Spring Modulith's Event Publication Registry is useful for tracking incomplete event publications, but it is not a full outbox implementation.
The registry records event publication state and allows failed publications to be resubmitted. However, production outbox concerns such as retry orchestration, multi-instance coordination, claiming, ordering, and operational dispatching are left to the application.
Namastack Outbox fills that gap.
With its Namastack Outbox integration, Spring Modulith can delegate event externalization to a purpose-built outbox engine.
What Namastack Outbox provides
Namastack Outbox is designed for reliable event publication in production environments.
It provides:
- transactional outbox persistence
- safe dispatching from multiple application instances
- retry handling for failed publications
- order-preserving publication
- operationally robust event delivery
- separation between domain transaction and external broker delivery
This makes it suitable for applications that run with multiple replicas, publish to external brokers, and require reliable delivery semantics.
How it works
When an application event is externalized by Spring Modulith, Spring Modulith's Namastack Outbox integration records the outgoing event in the outbox.
The event is stored transactionally together with the business change. After the transaction commits, Namastack Outbox takes responsibility for dispatching the event to the configured external messaging infrastructure.
Conceptually:
- Your application changes domain state.
- A Spring Modulith application event is published.
- Spring Modulith detects that the event is externalized.
- In
outboxmode, Spring Modulith delegates the event to Namastack Outbox. - Namastack Outbox stores the event transactionally.
- Namastack Outbox dispatches the event reliably after commit.
- Failed dispatch attempts are retried by Namastack Outbox.
Event Publication Registry vs. Namastack Outbox
| Capability | Spring Modulith Event Publication Registry | Namastack Outbox |
|---|---|---|
| Tracks event publication state | Yes | Yes |
| Persists publication intent transactionally | Yes | Yes |
| Resubmits incomplete publications | Yes | Yes |
| Coordinates multiple application instances | User responsibility | Yes |
| Handles retry orchestration | User responsibility | Yes |
| Supports order-preserving publication | Not the primary purpose | Yes |
| Designed as production outbox infrastructure | No | Yes |
When to use Namastack Outbox
Use Namastack Outbox with Spring Modulith when:
- your application runs with multiple instances
- events are sent to an external broker
- failed event publication must be retried automatically
- duplicate concurrent dispatching must be avoided
- event ordering matters
- you want outbox behavior without building your own dispatcher
Migration in 3 Steps
Step 1: Add Dependencies
Add Spring Modulith's Namastack starter. Spring Modulith brings the required Namastack Outbox runtime support for outbox-backed event externalization.
The examples below use Spring Modulith 2.1.0-RC1, where Namastack Outbox support is available.
Use the current Spring Modulith 2.1.x version for your project and configure the matching Spring
milestone or snapshot repository if you are not using a GA release.
Choose your persistence layer:
JPA-based Projects
- Gradle
- Maven
dependencyManagement {
imports {
mavenBom("org.springframework.modulith:spring-modulith-bom:2.1.0-RC1")
}
}
dependencies {
// Your existing Spring Modulith dependencies
implementation("org.springframework.modulith:spring-modulith-starter-jpa")
// Your transport dependencies (e.g., Kafka)
implementation("org.springframework.modulith:spring-modulith-events-kafka")
// NEW: Spring Modulith's Namastack Outbox integration
implementation("org.springframework.modulith:spring-modulith-starter-namastack")
}
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-bom</artifactId>
<version>2.1.0-RC1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Your existing Spring Modulith dependencies -->
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-jpa</artifactId>
</dependency>
<!-- Your transport dependencies (e.g., Kafka) -->
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-events-kafka</artifactId>
</dependency>
<!-- NEW: Spring Modulith's Namastack Outbox integration -->
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-namastack</artifactId>
</dependency>
</dependencies>
JDBC-based Projects
- Gradle
- Maven
dependencyManagement {
imports {
mavenBom("org.springframework.modulith:spring-modulith-bom:2.1.0-RC1")
}
}
dependencies {
// Your existing Spring Modulith dependencies
implementation("org.springframework.modulith:spring-modulith-starter-jdbc")
// Your transport dependencies (e.g., Kafka)
implementation("org.springframework.modulith:spring-modulith-events-kafka")
// NEW: Spring Modulith's Namastack Outbox integration
implementation("org.springframework.modulith:spring-modulith-starter-namastack")
}
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-bom</artifactId>
<version>2.1.0-RC1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Your existing Spring Modulith dependencies -->
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-jdbc</artifactId>
</dependency>
<!-- Your transport dependencies (e.g., Kafka) -->
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-events-kafka</artifactId>
</dependency>
<!-- NEW: Spring Modulith's Namastack Outbox integration -->
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-namastack</artifactId>
</dependency>
</dependencies>
Step 2: Enable Outbox Mode
Enable Outbox Mode in your application.yml:
spring:
modulith:
events:
externalization:
mode: outbox # NEW: Switch to Outbox Mode
With those Spring Modulith dependencies and outbox mode enabled, Spring Modulith routes
@Externalized events through Namastack Outbox.
Step 3: Code Changes (Optional)
Your existing code works without any changes:
- Kotlin
- Java
@Externalized("payment-requests::#{orderId}")
data class PaymentRequestedEvent(
val paymentId: UUID,
val orderId: UUID,
val amountCents: Long
)
@Service
class PaymentService(
private val paymentRepository: PaymentRepository,
private val events: ApplicationEventPublisher
) {
@Transactional
fun requestPayment(orderId: UUID, amountCents: Long): Payment {
val payment = paymentRepository.save(
Payment.request(orderId, amountCents)
)
// Event is automatically written to the Outbox
events.publishEvent(
PaymentRequestedEvent(
paymentId = payment.id,
orderId = payment.orderId,
amountCents = payment.amountCents
)
)
return payment
}
}
@Externalized("payment-requests::#{orderId}")
public record PaymentRequestedEvent(
UUID paymentId,
UUID orderId,
Long amountCents
) {}
@Service
class PaymentService {
@Transactional
public Payment requestPayment(UUID orderId, Long amountCents) {
var payment = paymentRepository.save(
Payment.request(orderId, amountCents)
);
// Event is automatically written to the Outbox
events.publishEvent(new PaymentRequestedEvent(
payment.getId(),
payment.getOrderId(),
payment.getAmountCents()
));
return payment;
}
}
Example Project
See the Spring Modulith Outbox Example for a complete, runnable reference implementation.
This example demonstrates:
- Outbox-backed event externalization with Spring Modulith
- JPA persistence
- Kafka integration
- End-to-end event flow and configuration
Full Namastack Outbox Feature Set
All other features of Namastack Outbox are available and can be used seamlessly with the Spring Modulith integration. This includes:
- Advanced retry and backoff strategies
- Partitioning and multi-instance coordination
- Observability, metrics, and tracing
- Dead letter queue support
- Custom transports and delivery handlers
- Operational tooling and health endpoints
For details, see the Namastack Outbox documentation and the linked advanced configuration topics above.
Note on Messaging Integration
If you use Spring Modulith's messaging integration feature (e.g., Kafka, RabbitMQ, SNS), you do not need to define your own outbox handler. The outbox handler is automatically provided by Modulith. You can simply adjust the default retry policy and other outbox settings via configuration properties to fit your requirements.
The modulith-namastack module disables Namastack Outbox's OutboxEventMulticaster. Spring
Modulith's event multicaster takes precedence and is responsible for routing externalized events
through the outbox integration.
See Retry Policy for details on customizing default retry behavior.
Advanced Configuration
Compile-Time Dependencies for Customization
The Spring Modulith dependencies above are enough to use outbox-backed externalization. Add
Namastack dependencies directly only when your application code customizes Namastack Outbox with
Namastack API types, such as defining an OutboxRetryPolicy bean or a custom OutboxHandler.
- Gradle
- Maven
dependencies {
implementation(platform("io.namastack:namastack-outbox-bom:1.7.x"))
implementation("io.namastack:namastack-outbox-api")
// Add other Namastack modules only when you configure them directly
}
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.namastack</groupId>
<artifactId>namastack-outbox-bom</artifactId>
<version>1.7.x</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>io.namastack</groupId>
<artifactId>namastack-outbox-api</artifactId>
</dependency>
<!-- Add other Namastack modules only when you configure them directly -->