sagaExecutor

fun <C, R> sagaExecutor(configure: SagaBuilder<C, R>.() -> Unit): SagaExecutor<C, R>

Factory function for creating sagas with DSL.

This function provides a convenient way to create saga executors using Kotlin's DSL syntax.

Usage Example

val saga = sagaExecutor<OrderContext, OrderResult> {
step("reserve-inventory") { context ->
val reservationId = inventoryService.reserve(context.items) // Can be suspend
ReservationResult(reservationId)
}
compensate { result ->
inventoryService.release(result.reservationId) // Can be suspend
}

step("charge-payment") { context ->
val transactionId = paymentService.charge(context.amount) // Can be suspend
PaymentResult(transactionId)
}
compensate { result ->
paymentService.refund(result.transactionId) // Can be suspend
}
}

// Execute within a coroutine
runBlocking {
val result = saga.execute(OrderContext(items = listOf("item1"), amount = 99.99))
when (result) {
is SagaResult.Completed -> println("Success: ${result.value}")
is SagaResult.Aborted -> println("Aborted: ${result.error.message}")
is SagaResult.CompensationFailure -> println("Critical failure")
}
}

Return

A configured SagaExecutor ready for execution

Parameters

C

The type of context passed to saga steps

R

The type of result produced by saga steps

configure

Configuration block for the saga builder


fun <C, R, P> sagaExecutor(journal: SagaJournal<P>, config: JournalConfig = JournalConfig(), decodeEffectPayload: (P) -> R? = null, encodeEffectPayload: (R) -> P? = null, configure: SagaBuilder<C, R>.() -> Unit): SagaExecutor<C, R>

Factory function for creating journal-aware stateless saga executors with deferred RunId.

Unlike the sagaExecutor(journal, runId, ...) overload, this factory does not require a RunId at build time. The JournalInterceptor is constructed fresh on each call to SagaExecutor.execute(context, runId, ...), allowing the same executor instance to be reused across multiple saga runs with different identifiers.

When decodeEffectPayload is provided, F004 effect-key short-circuiting is active: before each step that declares an effectKey, the executor scans the journal for a prior ca.acendas.kstate.saga.journal.EntryPhase.Effect entry with a matching key and step name. If found, the step's forward lambda is skipped and the decoded result is used instead.

F004 collision detection is always active when a journal is present — it does not require a payload decoder.

Return

A configured SagaExecutor that defers journal wiring until execute-time.

Parameters

C

The context type passed to saga steps.

R

The result type produced by saga steps.

P

The payload type used by the journal.

journal

The journal to append entries to.

config

Journal configuration (defaults to JournalConfig with Hashing.None).

decodeEffectPayload

Optional decoder that extracts a step result R from a journal payload P. Required to enable F004 short-circuiting. Pass null to disable.

configure

Configuration block for the saga builder.


fun <C, R, P> sagaExecutor(journal: SagaJournal<P>, runId: RunId, config: JournalConfig = JournalConfig(), decodeEffectPayload: (P) -> R? = null, configure: SagaBuilder<C, R>.() -> Unit): SagaExecutor<C, R>
fun <C, R, S : Any, P> sagaExecutor(initialState: S, journal: SagaJournal<P>, runId: RunId, config: JournalConfig = JournalConfig(), decodeEffectPayload: (P) -> R? = null, configure: StatefulSagaBuilder<C, R, S>.() -> Unit): StatefulSagaExecutor<C, R, S>


fun <C, R, S : Any> sagaExecutor(initialState: S, configure: StatefulSagaBuilder<C, R, S>.() -> Unit): StatefulSagaExecutor<C, R, S>

Factory function for creating stateful sagas with typed state management.

This overload of sagaExecutor creates a saga with shared state that is accessible in both forward actions and compensation logic. The state type is inferred from the initialState parameter.

Usage Example

data class PaymentState(
val paymentAmount: Long = 0L,
val escrowCaptured: Boolean = false
)

val saga = sagaExecutor<Cart, Order>(PaymentState()) {
first `do` "take-payment" with { cart ->
updateState { it.copy(paymentAmount = payment.amount) }
createOrder(cart)
} and undo {
if (state.escrowCaptured) refund() else returnEscrow()
}

then `do` "capture-escrow" with { cart ->
cashManager.captureEscrow()
updateState { it.copy(escrowCaptured = true) }
result!!
}
}

Return

A configured StatefulSagaExecutor ready for execution

Parameters

C

The type of context passed to saga steps

R

The type of result produced by saga steps

S

The type of shared saga state (inferred from initialState)

initialState

Initial value for the saga state

configure

Configuration block for the saga builder


fun <C, R, S : Any, P> sagaExecutor(initialState: S, journal: SagaJournal<P>, config: JournalConfig = JournalConfig(), decodeEffectPayload: (P) -> R? = null, effectResolver: EffectResolver<R>? = null, encodeEffectPayload: (R) -> P? = null, resumeStateReducer: StateReducer<S, P>? = null, resumeTerminalDecoder: TerminalDecoder<P, R>? = null, resumeTerminalEncoder: TerminalEncoder<P>? = null, configure: StatefulSagaBuilder<C, R, S>.() -> Unit): StatefulSagaExecutor<C, R, S>

Factory function for creating journal-aware stateful saga executors with deferred RunId.

Unlike the sagaExecutor(initialState, journal, runId, ...) overload, this factory does not require a RunId at build time. The JournalInterceptor is constructed fresh on each call to StatefulSagaExecutor.execute(context, runId, ...), allowing the same executor instance to be reused across multiple saga runs with different identifiers.

When decodeEffectPayload is provided, F004 effect-key short-circuiting is active: before each step that declares an effectKey, the executor scans the journal for a prior ca.acendas.kstate.saga.journal.EntryPhase.Effect entry with a matching key and step name. If found, the step's forward lambda is skipped and the decoded result is used instead.

Return

A configured StatefulSagaExecutor that defers journal wiring until execute-time.

Parameters

C

The context type passed to saga steps.

R

The result type produced by saga steps.

S

The saga state type (must be non-null).

P

The payload type used by the journal.

initialState

Initial value for the saga state.

journal

The journal to append entries to.

config

Journal configuration (defaults to JournalConfig with Hashing.None).

decodeEffectPayload

Optional decoder that extracts a step result R from a journal payload P. Required to enable F004 short-circuiting. Pass null to disable.

configure

Configuration block for the saga builder.