Skip to content
This repository has been archived by the owner on Jun 6, 2024. It is now read-only.

nomisRev/Saga

Repository files navigation

Module Saga

Maven Central Latest snapshot Website can be found here

Add in build.gradle.kts

repositories {
    mavenCentral()
}

dependencies {
    implementation("io.github.nomisrev:Saga:1.0.1")
}

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 [action]s 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(action = { createOrder() }, compensation = { ::deleteOrder })
    val payment = saga(action = { createPayment(order) }, compensation = { ::deletePayment) })
    payment.awaitSuccess()
  }.transact()
}

// Deleting Payment(id=5753e9bb-248a-4385-8c9c-4a524e80c0f9, orderId=3a55ffab-a3f5-40a9-a2b3-681dc17b174e)
// Deleting Order(id=3a55ffab-a3f5-40a9-a2b3-681dc17b174e, amount=100)
// Exception in thread "main" java.lang.RuntimeException: Payment Failed
//   at io.github.nomisrev.TestKt.awaitSuccess(test.kt:11)