Saga

interface Saga<A>

The saga design pattern is a way to manage data consistency across microservices in distributed transaction scenarios. A Saga is useful when you need to manage data in a consistent manner across services in distributed transaction scenarios. Or when you need to compose multiple actions with a compensation that needs to run in a transaction like style.

For example, let's say that we have following domain types Order, Payment.

data class Order(val id: UUID, val amount: Long)
data class Payment(val id: UUID, val orderId: UUID)

The creation of an Order can only remain when a payment has been made. In SQL you might run this inside a transaction, which can automatically rollback the creation of the Order when the creation of the Payment fails.

When you need to do this across distributed services, or a multiple atomic references, etc you need to manually facilitate the rolling back of the performed actions, or compensating actions.

The Saga type, and saga DSL remove all the boilerplate of manually having to facilitate this with a convenient suspending DSL.

data class Order(val id: UUID, val amount: Long)
suspend fun createOrder(): Order = Order(UUID.randomUUID(), 100L)
suspend fun deleteOrder(order: Order): Unit = println("Deleting $order")

data class Payment(val id: UUID, val orderId: UUID)
suspend fun createPayment(order: Order): Payment = Payment(UUID.randomUUID(), order.id)
suspend fun deletePayment(payment: Payment): Unit = println("Deleting $payment")

suspend fun Payment.awaitSuccess(): Unit = throw RuntimeException("Payment Failed")

suspend fun main() {
saga {
val order = saga { createOrder() }.compensate(::deleteOrder).bind()
val payment = saga { createPayment(order) }.compensate(::deletePayment).bind()
payment.awaitSuccess()
}.transact()
}

Types

Builder
Link copied to clipboard
value class Builder<A>(f: suspend SagaEffect.() -> A) : Saga<A>
Wrapper around the saga { } builder.
Full
Link copied to clipboard
class Full<A>(action: suspend SagaEffect.() -> A, compensation: suspend (A) -> Unit) : Saga<A>
Full for a Saga that defines both action and Full.compensation.
Part
Link copied to clipboard
value class Part<A>(action: suspend SagaEffect.() -> A) : Saga<A>
A partial Saga, tt only defines the action.

Functions

compensate
Link copied to clipboard
open infix fun compensate(compensate: suspend (A) -> Unit): Saga<A>
Add a compensating action to a Saga.
parZip
Link copied to clipboard
open fun <B, C> parZip(other: Saga<B>, transform: suspend CoroutineScope.(A, B) -> C): Saga<C>
open fun <B, C, D> parZip(ctx: CoroutineContext, b: Saga<B>, c: Saga<C>, f: suspend CoroutineScope.(A, B, C) -> D): Saga<D>
open fun <B, C, D, E> parZip(ctx: CoroutineContext, b: Saga<B>, c: Saga<C>, d: Saga<D>, f: suspend CoroutineScope.(A, B, C, D) -> E): Saga<E>
open fun <B, C, D, E, F> parZip(ctx: CoroutineContext, b: Saga<B>, c: Saga<C>, d: Saga<D>, e: Saga<E>, f: suspend CoroutineScope.(A, B, C, D, E) -> F): Saga<F>
open fun <B, C, D, E, F, G> parZip(ctx: CoroutineContext, b: Saga<B>, c: Saga<C>, d: Saga<D>, e: Saga<E>, ff: Saga<F>, f: suspend CoroutineScope.(A, B, C, D, E, F) -> G): Saga<G>
open fun <B, C, D, E, F, G, H> parZip(ctx: CoroutineContext, b: Saga<B>, c: Saga<C>, d: Saga<D>, e: Saga<E>, ff: Saga<F>, g: Saga<G>, f: suspend CoroutineScope.(A, B, C, D, E, F, G) -> H): Saga<H>
open fun <B, C, D, E, F, G, H, I> parZip(ctx: CoroutineContext, b: Saga<B>, c: Saga<C>, d: Saga<D>, e: Saga<E>, ff: Saga<F>, g: Saga<G>, h: Saga<H>, f: suspend CoroutineScope.(A, B, C, D, E, F, G, H) -> I): Saga<I>
open fun <B, C> parZip(ctx: CoroutineContext, other: Saga<B>, transform: suspend CoroutineScope.(A, B) -> C): Saga<C>
Runs multiple Sagas in parallel and combines the result with the transform function.
transact
Link copied to clipboard
open suspend fun transact(): A
Transact runs the Saga turning it into a suspend effect that results in A.

Inheritors

Saga
Link copied to clipboard
Saga
Link copied to clipboard
Saga
Link copied to clipboard

Sources

jvm source
Link copied to clipboard