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?"
Imagine um processo de abertura de conta que manda toda solicitação direto para o mesmo analista. Um gateway resolve isso: logo depois do cálculo do 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" : "NORMAL";
}
}
{
"id": "GATEWAY_FAIXA_RISCO",
"name": "Qual a faixa de risco?",
"type": "EXCLUSIVE_GATEWAY",
"providerType": "BEAN",
"providerBean": "faixaRiscoAnswerProvider",
"outgoing": [
{ "id": "flow-comum", "targetNodeId": "APROVAR_ABERTURA", "isDefault": true },
{ "id": "flow-mesa", "targetNodeId": "ANALISE_MESA", "expectedAnswer": "ALTO" }
]
}
Score de risco alto (< 700) segue para Análise de Mesa; qualquer outra resposta (NORMAL, ou um
valor novo que o provider passe a devolver no futuro) 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": "APROVAR_ABERTURA", "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 — uma resposta null nunca cai na aresta isDefault: sem uma aresta
handlesNull, ela vira IllegalStateException em execução. No exemplo da faixa de risco, o caminho
comum (NORMAL) é o default, não um expectedAnswer explícito.
Declarar no Craft
No Craft, o gateway é um Decision. Ao conectar a saída de um Decision, a conexão nasce exigindo
configuração de rota — marque uma como rota padrão (isDefault) e dê à outra o valor esperado
(ALTO). O gateway providerType: VARIABLE não precisa de bean nenhum: ele lê uma variável que a tarefa
externa anterior já gravou.
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, lembretes — veja Timers e Prazos.