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.
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?"
Um score baixo é uma decisão de roteamento — o caso é válido, só
segue por outro caminho. Um CPF com formato inválido é diferente: Calcular 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):
@Component("calcularScoreTaskHandler")
public class CalcularScoreTaskHandler implements TaskHandler {
@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");
}
// ... consulta o bureau e grava o score normalmente
}
}
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": "EXECUTABLE_A1B2C3D4",
"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.
errorCode como parte do contrato público do seu processoDa 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.
Modelar no Craft
Selecione Calcular Score 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. Ligue a saída do
handler ao nó que trata o desvio (Solicitar Correção de Dados, um External Task).
Implementar
Nenhum bean novo — o erro é sinalizado de dentro do CalcularScoreTaskHandler que você já tem, com
um throw new ProcessErrorException(...). O BOUNDARY_ERROR_HANDLER é puramente modelagem no
.kikwi.
Operar no Monitor
Inicie uma instância com um cpf malformado de propósito. No canvas ao vivo, Calcular Score
é interrompida e o fluxo acende o caminho de Solicitar Correção de Dados — sem incidente, sem
stacktrace. Compare com iniciar uma instância que provoque uma falha técnica no handler (uma
exceção comum, não ProcessErrorException): aí sim o Monitor mostra um incidente aberto, com a
mensagem de erro e o botão de retry.
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 todos leem e produzem estado através de variáveis de processo — veja como elas funcionam em Variáveis de Processo.