SagaBuilder
The fluent DSL for defining saga steps.
API
public interface SagaBuilder<C> {
/**
* Begins the definition of a new step.
*/
StepBuilder<C> step(String name);
/**
* Finalizes the saga definition.
*/
void build();
}
StepBuilder
Each step is configured through StepBuilder:
public interface StepBuilder<C> {
/**
* Defines the step execution function.
*/
StepBuilder<C> invoke(Function<C, C> action);
/**
* Defines the compensation function (rollback).
*/
StepBuilder<C> compensate(Consumer<C> compensation);
/**
* Configures the retry policy.
*/
StepBuilder<C> retry(RetryPolicy policy);
/**
* Adds a timeout to the step.
*/
StepBuilder<C> timeout(Duration timeout);
/**
* Defines a condition for step execution.
*/
StepBuilder<C> when(Predicate<C> condition);
/**
* Begins the next step (chaining).
*/
StepBuilder<C> step(String name);
/**
* Finalizes the definition.
*/
void build();
}
Examples
Basic Step
builder
.step("validate")
.invoke(this::validate)
.build();
Step with Compensation
builder
.step("block-balance")
.invoke(this::blockBalance)
.compensate(this::unblockBalance)
.build();
Step with Retry
builder
.step("call-external-api")
.invoke(this::callApi)
.compensate(this::rollbackApi)
.retry(exponential(3, Duration.ofSeconds(1)))
.build();
Step with Timeout
builder
.step("slow-operation")
.invoke(this::slowOperation)
.timeout(Duration.ofSeconds(30))
.build();
Conditional Step
builder
.step("send-premium-notification")
.invoke(this::sendPremiumNotification)
.when(ctx -> ctx.isPremiumUser())
.build();
Full Chain
@Override
public void define(SagaBuilder<OrderContext> builder) {
builder
// Step 1: COMPENSABLE
.step("reserve-stock")
.invoke(this::reserveStock)
.compensate(this::releaseStock)
.timeout(Duration.ofSeconds(5))
// Step 2: COMPENSABLE
.step("process-payment")
.invoke(this::processPayment)
.compensate(this::refundPayment)
.retry(exponential(3, Duration.ofSeconds(2)))
// Step 3: PIVOT (no compensation)
.step("confirm-order")
.invoke(this::confirmOrder)
.retry(exponential(5, Duration.ofSeconds(1)))
// Step 4: CONDITIONAL
.step("send-vip-gift")
.invoke(this::sendVipGift)
.when(ctx -> ctx.isVipCustomer())
// Step 5: RETRIABLE
.step("send-confirmation")
.invoke(this::sendConfirmation)
.retry(infinite())
.build();
}
Invoke Methods
Signature
Function<C, C> action
The method receives the current context and must return a new context (immutability).
Example
private OrderContext reserveStock(OrderContext ctx) {
// Executes the operation
String reservationId = stockService.reserve(ctx.items());
// Returns new context with the result
return ctx.withReservationId(reservationId);
}
Compensate Methods
Signature
Consumer<C> compensation
The method receives the context and returns nothing (void).
Example
private void releaseStock(OrderContext ctx) {
// Uses data saved in the context to revert
stockService.release(ctx.reservationId());
}
Kotlin
The Java builder has overloaded invoke() and compensate() methods that cause SAM conversion ambiguity in Kotlin. The sagaweaw-kotlin module provides a block-based DSL that resolves this:
// Java — requires explicit Consumer<T>
saga.step("charge-payment")
.invoke(Consumer { ctx -> paymentService.charge(ctx.amount) })
.compensate(Consumer { ctx -> paymentService.refund(ctx.orderId) })
// Kotlin DSL — clean lambdas, no type annotations
saga.step("charge-payment") {
invoke { ctx -> paymentService.charge(ctx.amount) }
compensate { ctx -> paymentService.refund(ctx.orderId) }
retry(exponentialRetry(3, 5.seconds))
}
See the Kotlin guide → for setup.