Package io.github.nomisrev

Types

Link copied to clipboard
typealias Saga<A> = suspend SagaEffect.() -> 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 the 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 roll back 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() }) { deleteOrder(it) }
val payment = saga { createPayment(order) }, ::deletePayment)
payment.awaitSuccess()
}.transact()
}
Link copied to clipboard
object SagaActionStep

Marker object to protect SagaEffect.saga from calling SagaEffect.bind in its action step.

Link copied to clipboard
annotation class SagaDSLMarker

DSL Marker for the SagaEffect DSL

Link copied to clipboard
interface SagaEffect

DSL that enables the Saga pattern in a suspend DSL.

Functions

Link copied to clipboard
inline fun <A> saga(noinline block: suspend SagaEffect.() -> A): Saga<A>

The Saga builder which exposes the SagaEffect.bind. The saga builder uses the suspension system to run actions, and automatically register their compensating actions.

fun <A> saga(action: suspend SagaActionStep.() -> A, compensation: suspend (A) -> Unit): Saga<A>

Create a lazy Saga that will only run when the Saga is invoked.

Link copied to clipboard
suspend fun <A> Saga<A>.transact(): A

Transact runs the Saga turning it into a suspend effect that results in A. If the saga fails then all compensating actions are guaranteed to run. When a compensating action failed it will be ignored, and the other compensating actions will continue to be run.