Decisões e Gateways
Um EXCLUSIVE_GATEWAY roteia o fluxo para exatamente um ramo de saída, com base em uma decisão. A
decisão em si nunca é uma expressão embutida em string — é sempre o retorno de um método Java, ou o
valor de uma variável de processo já calculada.
A mesma entrada leva a caminhos completamente diferentes dependendo de uma avaliação objetiva: casos graves seguem para um caminho, casos leves para outro. A avaliação acontece uma vez, no momento em que o paciente chega, com base em critérios definidos. A analogia para de funcionar se você esperar que a triagem reavalie o paciente continuamente — um gateway do Kikwiflow decide uma única vez, no instante em que o fluxo passa por ali, e não reconsidera a decisão depois.
"E se o score for baixo — quero mandar para uma mesa de análise?"
O processo de abertura de conta até aqui manda toda solicitação direto para o mesmo analista. Um gateway resolve isso: logo depois de Calcular Score, uma decisão de negócio separa quem segue para a aprovação comum de quem precisa passar por uma mesa de análise mais rígida antes.
O contrato AnswerProvider
@FunctionalInterface
public interface AnswerProvider {
/**
* Avalia o contexto atual e retorna uma String representando a hipótese de resposta.
* Retornar nulo é permitido, mas deve ser tratado explicitamente no modelo do processo.
*/
String resolve(EvaluationContext context);
}
As duas estratégias de providerType
Um EXCLUSIVE_GATEWAY resolve sua decisão de uma entre duas formas, declaradas via providerType:
providerType | Origem da decisão | Campo relevante |
|---|---|---|
BEAN | Bean Spring implementando AnswerProvider | providerBean |
VARIABLE | Uma variável de processo já calculada por um nó anterior | providerVariable |
providerType: BEAN — a faixa de risco
A decisão depende de uma regra de negócio (a faixa em que o score cai), então é código Java:
package com.empresa.processo.decision;
import io.kikwiflow.execution.api.context.EvaluationContext;
import io.kikwiflow.execution.api.provider.AnswerProvider;
import org.springframework.stereotype.Component;
@Component("faixaRiscoAnswerProvider")
public class FaixaRiscoAnswerProvider implements AnswerProvider {
@Override
public String resolve(EvaluationContext context) {
int score = (int) context.getVariableValue("score").orElseThrow();
return score < 700 ? "ALTO" : null; // null segue para a aresta default
}
}
{
"id": "GATEWAY-FAIXA-RISCO",
"name": "Qual a faixa de risco?",
"type": "EXCLUSIVE_GATEWAY",
"providerType": "BEAN",
"providerBean": "faixaRiscoAnswerProvider",
"outgoing": [
{ "id": "flow-comum", "targetNodeId": "EXTERNAL_E5F3809A", "isDefault": true },
{ "id": "flow-mesa", "targetNodeId": "ANALISE_MESA", "expectedAnswer": "ALTO" }
]
}
Score de risco alto (< 700) segue para Análise de Mesa; qualquer outro caso cai na aresta
isDefault e vai direto para Aprovar Abertura.
providerType: VARIABLE — o resultado da mesa
Quando a resposta já foi calculada por um nó anterior — por exemplo, um analista sênior que digitou a decisão ao completar a tarefa externa Análise de Mesa — o gateway só precisa lê-la como variável:
{
"id": "GATEWAY-RESULTADO-MESA",
"type": "EXCLUSIVE_GATEWAY",
"providerType": "VARIABLE",
"providerVariable": "resultadoMesa",
"outgoing": [
{ "id": "flow-mesa-aprova", "targetNodeId": "EXTERNAL_E5F3809A", "expectedAnswer": "APROVADA" },
{ "id": "flow-mesa-recusa", "targetNodeId": "END_RECUSADA", "isDefault": true }
]
}
Como a aresta de saída é escolhida
Dada a resposta resolvida (por BEAN ou VARIABLE), o motor escolhe a primeira aresta cujo
expectedAnswer seja igual (comparação exata de string); se a resposta for null, escolhe a aresta
com handlesNull: true; se nenhuma casar, escolhe a aresta isDefault: true, se existir.
isDefaultSem uma aresta isDefault, uma resposta inesperada (que não bate com nenhum expectedAnswer nem é
null) não tem para onde ir. Declare isDefault: true sempre que o AnswerProvider puder evoluir
com novos valores de resposta com o tempo, e handlesNull: true sempre que ele puder legitimamente
retornar null. No exemplo da faixa de risco, FaixaRiscoAnswerProvider retorna null para "risco
normal" de propósito — é o caso comum, e o caminho comum deve ser o default, não um
expectedAnswer explícito.
Modelar no Craft
Arraste um Decision (Exclusive Gateway) logo depois de Calcular Score e ligue-o a dois
caminhos. Ao conectar a saída de um Decision, a conexão nasce exigindo configuração de rota — marque
uma como rota padrão (default) e dê à outra o valor esperado (ALTO). Preencha o campo do
provider com faixaRiscoAnswerProvider. O nó Análise de Mesa é um External Task como qualquer
outro (ver Tarefas Externas).
Implementar
O FaixaRiscoAnswerProvider acima é o único bean novo — um @Component nomeado, resolvido por nome
igual ao providerBean do JSON, exatamente como um TaskHandler. O gateway providerType: VARIABLE
não precisa de bean nenhum: ele lê uma variável que a tarefa externa anterior já gravou.
Operar no Monitor
Inicie duas instâncias pelo Monitor: uma com
rendaDeclarada alta (score acima de 700) e outra com renda baixa. No canvas ao vivo, a primeira
segue direto para Aprovar Abertura; a segunda acende o caminho de Análise de Mesa. Clicar no
nó do gateway no diagrama filtra a dock de instâncias para as que passaram por ali — dá para ver, em
tempo real, quantas foram para cada lado.
Quando usar / quando não usar
Use um EXCLUSIVE_GATEWAY quando o fluxo precisa escolher um entre vários caminhos com base
em uma condição avaliada uma vez.
Não use quando:
- Você quer executar vários passos ao mesmo tempo (não escolher um) — isso é Processamento Paralelo.
- A decisão depende de esperar alguém — modele a espera como
EXTERNAL_TASKe leia o resultado num gatewayproviderType: VARIABLEdepois, como no exemplo do resultado da mesa acima.
Próximo passo
Gateways decidem para onde o fluxo vai. Para decidir quando algo acontece — prazos, expiração, o que fazer se o analista não decidir em 48h — veja Timers e Prazos.