Tipos de Steps
O Sagaweaw infere automaticamente o tipo de cada step baseado na sua definição. Existem três tipos:
COMPENSABLE
Um step COMPENSABLE possui um método compensate() definido. Se um step posterior falhar, o Sagaweaw executará a compensação automaticamente.
.step("block-balance")
.invoke(this::blockBalance)
.compensate(this::unblockBalance) // ← Define compensação
Características
| Aspecto | Comportamento |
|---|---|
| Compensação | Executada em ordem inversa |
| Dados | Contexto disponível para compensar |
| Retry | Configurável |
Exemplo Completo
// Invoke: Bloqueia o saldo
private PixContext blockBalance(PixContext ctx) {
String blockId = balanceService.block(ctx.payerId(), ctx.amount());
return ctx.withBlockId(blockId); // Salva o ID para compensação
}
// Compensate: Desbloqueia usando o blockId salvo
private void unblockBalance(PixContext ctx) {
balanceService.unblock(ctx.blockId()); // Usa o ID salvo
}
Sempre salve no contexto os dados necessários para compensar (IDs, tokens, etc).
PIVOT
Um step PIVOT é o "ponto de não retorno". Não possui compensação definida. Após a execução bem-sucedida de um PIVOT, os steps anteriores não serão compensados mesmo se steps posteriores falharem.
.step("transmit-to-bacen")
.invoke(this::transmitToBacen)
// Sem .compensate() = PIVOT
Características
| Aspecto | Comportamento |
|---|---|
| Compensação | Não existe |
| Ponto de não retorno | Sim |
| Retry | Geralmente configurado |
Quando Usar
- Operações irreversíveis: Envio de email, SMS, notificação push
- Integrações externas sem rollback: APIs de terceiros, BACEN, gateways
- Confirmações finais: Commit de transação externa
Exemplo
.step("transmit-to-bacen") // PIVOT
.invoke(this::transmitToBacen)
.retry(exponential(3, Duration.ofSeconds(1))) // Retry antes de desistir
Se um PIVOT falhar após todas as tentativas de retry, a saga entra em estado FAILED e os steps COMPENSABLE anteriores são compensados.
RETRIABLE
Um step RETRIABLE tem retry infinito e não possui compensação. Ele deve sempre eventualmente ter sucesso. Use para operações idempotentes que podem falhar temporariamente.
.step("send-notification")
.invoke(this::sendNotification)
.retry(infinite()) // ← Retry infinito sem compensate = RETRIABLE
Características
| Aspecto | Comportamento |
|---|---|
| Compensação | Não existe |
| Retry | Infinito |
| Expectativa | Sempre terá sucesso |
Quando Usar
- Notificações: Email, SMS, push (idempotentes)
- Atualizações de cache: Eventual consistency
- Webhooks: Notificar sistemas externos
Exemplo
.step("send-confirmation-email")
.invoke(this::sendConfirmationEmail)
.retry(infinite(Duration.ofMinutes(5), Duration.ofHours(1))) // Min 5min, max 1h entre tentativas
Só use RETRIABLE se a operação for idempotente (executar várias vezes produz o mesmo resultado).
Tabela Comparativa
| Tipo | compensate() | retry() | Quando Usar |
|---|---|---|---|
| COMPENSABLE | Definido | Opcional | Operações reversíveis |
| PIVOT | Não tem | Recomendado | Ponto de não retorno |
| RETRIABLE | Não tem | infinite() | Deve sempre ter sucesso |
Fluxo de Inferência
O Sagaweaw determina o tipo automaticamente:
┌─────────────────────────────────────────┐
│ Step definido │
└─────────────────┬───────────────────────┘
│
▼
┌─────────────────┐
│ Tem compensate? │
└────────┬────────┘
│
┌───────┴───────┐
│ │
SIM NÃO
│ │
▼ ▼
COMPENSABLE ┌─────────────────┐
│ Retry infinito? │
└────────┬────────┘
│
┌───────┴───────┐
│ │
SIM NÃO
│ │
▼ ▼
RETRIABLE PIVOT