Pular para o conteúdo principal

SagaContext

O objeto imutável que viaja entre os steps da saga.


Conceito

O SagaContext é um padrão, não uma interface. Você define seu próprio context como um record ou classe imutável.


Implementação com Record

public record OrderContext(
// Dados de entrada (definidos na criação)
String orderId,
String customerId,
List<OrderItem> items,
BigDecimal totalAmount,

// Outputs dos steps (populados durante execução)
String stockReservationId,
String paymentIntentId,
String shippingTrackingId
) {

/**
* Cria um contexto inicial com apenas os dados de entrada.
*/
public static OrderContext create(Order order) {
return new OrderContext(
order.getId(),
order.getCustomerId(),
List.copyOf(order.getItems()),
order.getTotal(),
null, null, null // Outputs iniciam nulos
);
}

// --- Builders imutáveis ---

public OrderContext withStockReservationId(String id) {
return new OrderContext(
orderId, customerId, items, totalAmount,
id, paymentIntentId, shippingTrackingId
);
}

public OrderContext withPaymentIntentId(String id) {
return new OrderContext(
orderId, customerId, items, totalAmount,
stockReservationId, id, shippingTrackingId
);
}

public OrderContext withShippingTrackingId(String id) {
return new OrderContext(
orderId, customerId, items, totalAmount,
stockReservationId, paymentIntentId, id
);
}
}

Implementação com Lombok

@Value
@With
@Builder
public class OrderContext {
String orderId;
String customerId;
List<OrderItem> items;
BigDecimal totalAmount;

String stockReservationId;
String paymentIntentId;
String shippingTrackingId;

public static OrderContext create(Order order) {
return OrderContext.builder()
.orderId(order.getId())
.customerId(order.getCustomerId())
.items(List.copyOf(order.getItems()))
.totalAmount(order.getTotal())
.build();
}
}

// Uso
ctx.withStockReservationId("RES-123");

Requisitos

Deve ser

  • Serializável para JSON — O Sagaweaw persiste como JSONB
  • Imutável — Cada step retorna uma nova instância
  • Sem referências circulares — JSON não suporta

Evite

  • Coleções mutáveis (ArrayList, HashMap)
  • Referências a entidades JPA
  • Objetos complexos não serializáveis

Serialização

O context é serializado automaticamente:

{
"orderId": "ORD-9912",
"customerId": "CUST-001",
"items": [
{"productId": "PROD-1", "quantity": 2}
],
"totalAmount": 299.90,
"stockReservationId": "RES-123",
"paymentIntentId": "pi_abc123",
"shippingTrackingId": null
}

Uso nos Steps

Invoke

private OrderContext reserveStock(OrderContext ctx) {
// Usa dados do contexto
String reservationId = stockService.reserve(ctx.items());

// Retorna NOVO contexto com o resultado
return ctx.withStockReservationId(reservationId);
}

Compensate

private void releaseStock(OrderContext ctx) {
// Usa o ID salvo anteriormente
stockService.release(ctx.stockReservationId());
}

Relacionados