Pular para o conteúdo principal

Tratando Erros de Negócio

O Kikwiflow distingue explicitamente erros técnicos (bugs, falhas de infraestrutura — que seguem retry/incidentes, um assunto de operação, não de modelagem do processo — ver Retentativas) de erros de negócio esperados — uma validação que falhou, uma regra que rejeitou o caso. Este segundo tipo pode ser modelado como parte do próprio fluxo do processo, através de ProcessErrorException e nós BOUNDARY_ERROR_HANDLER.

Pense em uma correspondência devolvida ao remetente

Um envelope com CEP inválido não é um problema dos Correios — é uma informação que o remetente forneceu errada. A correspondência não se perde: ela volta, com um motivo específico ("CEP não localizado"), e o remetente decide o que fazer a seguir. A analogia para de funcionar na velocidade: o Kikwiflow trata o erro de negócio no mesmo instante, de forma síncrona, dentro do próprio fluxo — não dias depois, em uma devolução física.

"E se o CPF vier com formato inválido?"​

Num processo de abertura de conta, um score baixo é uma decisão de roteamento — o caso é válido, só segue por outro caminho. Um CPF com formato inválido é diferente: a etapa que calcula o score não deveria nem conseguir consultar o bureau com aquele dado. É esse segundo caso que ProcessErrorException modela.

Sinalizando um erro de negócio: ProcessErrorException​

public class ProcessErrorException extends RuntimeException {
public ProcessErrorException(String errorCode, String message) { /* ... */ }
public ProcessErrorException(String errorCode) { /* ... */ }
public String getErrorCode() { /* ... */ }
}

Qualquer TaskHandler pode lançar essa exceção para sinalizar que a execução daquele nó terminou em um estado esperado pelo modelo do processo, identificado por um errorCode de negócio (não uma classe Java):

package com.empresa.processo.executors;

import io.kikwiflow.exception.ProcessErrorException;
import io.kikwiflow.execution.api.context.ExecutionContext;
import io.kikwiflow.execution.api.handler.TaskHandler;
import io.kikwiflow.model.execution.ProcessVariable;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

@Component("calcularScoreTaskHandler")
public class CalcularScoreTaskHandler implements TaskHandler {

private final BureauClient bureauClient;

public CalcularScoreTaskHandler(BureauClient bureauClient) {
this.bureauClient = bureauClient;
}

@Override
public void handle(ExecutionContext execution) {
String cpf = execution.getVariable("cpf").value().toString();
if (!isCpfValido(cpf)) {
throw new ProcessErrorException("CPF_INVALIDO", "CPF fora do padrão esperado");
}

BigDecimal renda = new BigDecimal(execution.getVariable("rendaDeclarada").value().toString());
int scoreBureau = bureauClient.consultarScore(cpf);
int score = Math.min(scoreBureau + renda.divide(BigDecimal.TEN).intValue(), 1000);

execution.setVariable("score", new ProcessVariable("score", score));
}

private boolean isCpfValido(String cpf) {
return cpf.matches("\\d{11}");
}
}

É o mesmo handler de Tarefas Executáveis, com a validação na entrada.

Capturando o erro no fluxo: BOUNDARY_ERROR_HANDLER​

Anexe um BOUNDARY_ERROR_HANDLER ao nó que pode lançar o erro, via attachedToRef, exatamente como faria com um timer de borda:

{
"id": "error-handler-cpf-invalido",
"type": "BOUNDARY_ERROR_HANDLER",
"attachedToRef": "CALCULAR_SCORE",
"errorCode": "CPF_INVALIDO",
"outgoing": [ { "targetNodeId": "SOLICITAR_CORRECAO_DADOS" } ]
}

Quando o errorCode lançado casa com o errorCode declarado no handler, o fluxo é desviado para a saída do handler de erro — a tarefa original (Calcular Score) é interrompida, mas o processo continua normalmente a partir dali (abrindo uma tarefa externa para o solicitante reenviar os dados, por exemplo), não é tratado como falha técnica.

Um BOUNDARY_ERROR_HANDLER com errorCode omitido (null) funciona como um handler curinga, capturando qualquer ProcessErrorException lançada por aquele nó, independentemente do código específico. Declare um handler com errorCode explícito quando o processo precisa reagir de forma diferente a códigos distintos; declare um handler sem errorCode como fallback genérico.

O que acontece sem um handler correspondente​

Se nenhum BOUNDARY_ERROR_HANDLER casar com o errorCode lançado, a exceção não é engolida silenciosamente — ela é tratada como uma falha técnica, abrindo um incidente (sem retry automático, já que insistir em reexecutar o mesmo handler não vai fazer uma regra de negócio mudar de ideia). Ou seja: declarar o handler de borda é o que torna um erro de negócio parte do fluxo modelado, em vez de virar um incidente operacional que alguém do time de sustentação precisa investigar manualmente.

Trate errorCode como parte do contrato público do seu processo

Da mesma forma que trataria códigos de erro HTTP em uma API REST: documente os errorCodes junto à definição do processo, e evite reutilizar o mesmo código para situações semanticamente diferentes — o casamento é por igualdade exata de string ("cpf_invalido" é diferente de "CPF_INVALIDO"), então qualquer inconsistência de nomenclatura silenciosamente vira um incidente técnico em vez de ser tratado pelo fluxo.

Declarar no Craft​

No Craft, selecione o nó que pode lançar o erro e arraste um error handler para a borda dele. No campo do handler, informe o errorCode (CPF_INVALIDO) — ou deixe em branco para o handler curinga. Nenhum bean novo é necessário: o BOUNDARY_ERROR_HANDLER é puramente modelagem no .kikwi.

Quando usar / quando não usar​

Use ProcessErrorException + BOUNDARY_ERROR_HANDLER quando o "erro" é um resultado previsto pelo negócio — uma validação que reprovou, uma regra que rejeitou — e o processo tem um caminho definido para ele.

Não use para falhas técnicas (timeout, indisponibilidade, bug). Essas devem seguir retry e incidente — reexecutar o mesmo handler tem chance de dar certo; reexecutá-lo esperando uma regra de negócio mudar de ideia, não.

Próximo passo​

Erros de negócio, decisões e timers leem e produzem estado através de variáveis de processo — veja como elas funcionam em Variáveis de Processo.