Library · Kotlin · Spring Boot · Saga
bk-spring-saga
Spring Boot용 Kotlin-first, coroutine-native Saga 오케스트레이터. step { action { ... }; compensate { ... } } DSL로 분산 트랜잭션을 표현하고, 실패 시 역순 보상 트랜잭션을 자동 실행, JVM 크래시 후에도 다음 인스턴스가 중단된 지점부터 재개합니다. 김빌(Bill Kim)이 직접 설계하고 메인테이닝하는 오픈소스 라이브러리입니다.
한 줄 요약
step { action { ... }; compensate { ... } } DSL로 분산 트랜잭션을 정의하고, 실패 시 보상 트랜잭션을 역순으로 자동 실행. JVM 재시작 후에도 중단된 사가가 다음 인스턴스에서 이어집니다.
val orderSaga = saga<OrderContext>("order-fulfillment") {
step("reserve-inventory") {
action { ctx -> ctx.copy(reservationId = inventory.reserve(ctx)) }
compensate { ctx -> inventory.release(ctx.reservationId!!) }
retry(RetryPolicy.Exponential(maxAttempts = 3, initial = 100.milliseconds))
}
step("charge-payment") {
action { ctx -> ctx.copy(chargeId = payment.charge(ctx.userId, ctx.amountCents)) }
compensate { ctx -> payment.refund(ctx.chargeId!!) }
}
step("create-shipment") {
action { ctx -> ctx.copy(shipmentId = shipping.createShipment(ctx.orderId)) }
}
}
val result: SagaResult<OrderContext> = executor.execute(orderSaga, OrderContext(...))
create-shipment가 실패하면 charge-payment와 reserve-inventory의 보상이 역순으로 실행됩니다. JVM이 step 사이에서 죽으면 다음 인스턴스가 부분 실행된 saga를 이어받아 중단된 지점부터 재개합니다.
왜 만들었나
분산 사가는 모든 비-트리비얼 마이크로서비스에 등장합니다. Kotlin/Spring 옵션은 다음과 같았습니다:
- Axon / Eventuate Tram — heavyweight, 의견이 강한 프레임워크. 이벤트 스토어 + 메시지 브로커 + 런타임 모델까지 끌고 옵니다. Kotlin 친화적이지 않음.
- Camunda / Temporal — 훌륭하지만 워크플로 엔진. 별도 클러스터, 별도 운영 표면, 사이드카 프로세스에서 워크플로 작성.
- 직접 만들기 — 대부분의 팀이 매번 retry, 보상 순서, 낙관적 락, 재시작 시 재개를 처음부터 재발명합니다.
bk-spring-saga는 인-프로세스, 라이트웨이트 중간 옵션입니다. 워크플로 엔진이 아닙니다 — 타이머도, 시그널도, 병렬 분기도 없습니다. 선형 forward → 실패 시 보상 — 80% 케이스를 위한 가장 작은 도구입니다.
주요 기능
- 순수 Kotlin DSL —
step { action { ... }; compensate { ... } }. XML 없음, 어노테이션 없음. - 전 구간
suspend— WebFlux, coroutines, structured concurrency와 자연스럽게 통합. - 기본값으로 영속 — 모든 step 경계가 내구성 있게 기록. 시작 시 재개 실행.
- 플러그러블 스토리지 — JDBC 기본 제공, 테스트용 in-memory, Redis / R2DBC / Mongo는 SPI 구현.
- Spring Boot autoconfigure — starter 추가 후
@Bean SagaDefinition<Ctx>선언,SagaExecutor주입. - Micrometer 메트릭 기본 노출.