Skip to main content

SagaDefinition

The base interface that every saga must implement.


Definition

public interface SagaDefinition<C> {

/**
* Defines the saga steps using the builder.
*
* @param builder The fluent builder for configuring steps
*/
void define(SagaBuilder<C> builder);
}

Type Parameters

ParameterDescription
CThe saga context type (must be serializable to JSONB)

Implementation Example

@Saga("pix-payment")
public class PixPaymentSaga implements SagaDefinition<PixContext> {

private final DictService dictService;
private final BalanceService balanceService;
private final BacenService bacenService;

public PixPaymentSaga(
DictService dictService,
BalanceService balanceService,
BacenService bacenService
) {
this.dictService = dictService;
this.balanceService = balanceService;
this.bacenService = bacenService;
}

@Override
public void define(SagaBuilder<PixContext> builder) {
builder
.step("validate-dict")
.invoke(this::validateDict)
.compensate(this::invalidateDict)

.step("block-balance")
.invoke(this::blockBalance)
.compensate(this::unblockBalance)

.step("transmit-to-bacen")
.invoke(this::transmitToBacen)
.retry(exponential(3, Duration.ofSeconds(1)))

.build();
}

// --- Invoke Methods ---

private PixContext validateDict(PixContext ctx) {
String token = dictService.validate(ctx.dictKey());
return ctx.withValidationToken(token);
}

private PixContext blockBalance(PixContext ctx) {
String blockId = balanceService.block(ctx.payerId(), ctx.amount());
return ctx.withBlockId(blockId);
}

private PixContext transmitToBacen(PixContext ctx) {
bacenService.transmit(ctx.transactionId(), ctx.amount());
return ctx;
}

// --- Compensate Methods ---

private void invalidateDict(PixContext ctx) {
dictService.invalidate(ctx.validationToken());
}

private void unblockBalance(PixContext ctx) {
balanceService.unblock(ctx.blockId());
}
}

️ @Saga Annotation

The @Saga annotation registers the definition in the Spring container:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Saga {

/**
* Unique saga name used for identification and triggering.
*/
String value();

/**
* Optional description for documentation.
*/
String description() default "";
}

Attributes

AttributeTypeRequiredDescription
valueStringUnique saga name
descriptionStringOptional description for documentation

Example

@Saga(
value = "pix-payment",
description = "Processes PIX payments with automatic compensation"
)
public class PixPaymentSaga implements SagaDefinition<PixContext> {
// ...
}

Important Rules

Do Not Use @Transactional

@Saga("pix-payment")
@Transactional // <DocIcon name="alert" /> WRONG! The engine manages transactions
public class PixPaymentSaga implements SagaDefinition<PixContext> {
// ...
}

Sagaweaw manages all transactions internally. Using @Transactional can cause unexpected behavior.


Dependency Injection

Sagaweaw is compatible with Spring dependency injection:

@Saga("order-processing")
public class OrderProcessingSaga implements SagaDefinition<OrderContext> {

private final StockService stockService;
private final PaymentService paymentService;

// Constructor injection (recommended)
public OrderProcessingSaga(
StockService stockService,
PaymentService paymentService
) {
this.stockService = stockService;
this.paymentService = paymentService;
}

@Override
public void define(SagaBuilder<OrderContext> builder) {
// ...
}
}