Modelo de Processo e Nós de Fluxo
Um processo Kikwiflow é representado em runtime por io.kikwiflow.model.definition.process.ProcessDefinition
— um record imutável carregando key, version, checksum, defaultStartPoint e, principalmente, um mapa
flowNodes: Map<String, FlowNodeDefinition> indexado pelo id de cada nó do grafo.
public record ProcessDefinition(
String id, String sla, Integer version, String key, String name, String description,
Map<String, FlowNodeDefinition> flowNodes, String defaultStartPoint,
String checksum, Map<String, String> extensionProperties) { ... }
O checksum é calculado pelo parser (kikwi-parser-jackson) a cada deploy; se o conteúdo enviado for
idêntico a uma versão já implantada para a mesma key, o deploy é um no-op e a versão existente é retornada —
isso é o que torna o autodeploy no boot da aplicação idempotente entre reinícios.
FlowNodeDefinition: uma interface selada
Todo nó do grafo implementa a interface selada (sealed interface) FlowNodeDefinition, o que significa que o
compilador Java conhece o conjunto fechado de subtipos possíveis — útil para switch/instanceof exaustivos
dentro do próprio motor e para IDEs oferecerem autocomplete preciso ao lidar com o modelo:
public sealed interface FlowNodeDefinition permits StartEventDefinition,
ExternalTaskDefinition, ExecutableTaskDefinition, EndEventDefinition,
ExclusiveGatewayDefinition, InterruptiveTimerEventDefinition, BoundaryEventDefinition,
ParallelGatewayDefinition, JoinGatewayDefinition, NonInterruptiveTimerEventDefinition,
ErrorHandlerDefinition, CallActivityDefinition, EventCatcherDefinition,
InterruptiveCatchEventDefinition, TimerTaskDefinition, EventThrowerDefinition {
String id();
String name();
String type();
String description();
Boolean commitAfter();
Boolean commitBefore();
List<SequenceFlowDefinition> outgoing();
Map<String, String> extensionProperties();
LayoutCoordinates layout();
}
Todo nó, portanto, carrega commitBefore/commitAfter — os dois campos que controlam se o motor deve parar e
persistir uma fronteira transacional antes ou depois de processar aquele nó. Esse mecanismo é central o
suficiente para merecer sua própria página: veja
Execução Síncrona e Assíncrona.
Dispatch polimórfico no JSON
O parser (kikwi-parser-jackson, classe FlowNodeDefinitionMixin) resolve qual subtipo Java instanciar a
partir do campo "type" de cada entrada em flowNodes:
type no JSON | Classe Java | Marca de interface adicional |
|---|---|---|
DEFAULT_START_EVENT | StartEventDefinition | — |
DEFAULT_END_EVENT | EndEventDefinition | — |
EXECUTABLE_TASK | ExecutableTaskDefinition | Executable |
EXTERNAL_TASK | ExternalTaskDefinition | WaitState |
EXCLUSIVE_GATEWAY | ExclusiveGatewayDefinition | — |
PARALLEL_GATEWAY | ParallelGatewayDefinition | — |
JOIN_GATEWAY | JoinGatewayDefinition | — |
BOUNDARY_INTERRUPTIVE_TIMER | InterruptiveTimerEventDefinition | BoundaryEventDefinition |
BOUNDARY_NON_INTERRUPTIVE_TIMER | NonInterruptiveTimerEventDefinition | BoundaryEventDefinition |
BOUNDARY_ERROR_HANDLER | ErrorHandlerDefinition | BoundaryEventDefinition |
EVENT_CATCHER | EventCatcherDefinition | WaitState |
BOUNDARY_INTERRUPTIVE_CATCH_EVENT | InterruptiveCatchEventDefinition | BoundaryEventDefinition |
TIMER_TASK | TimerTaskDefinition | — |
EVENT_THROWER | EventThrowerDefinition | — |
CALL_ACTIVITY_COORDINATOR | CallActivityDefinition | — |
As marcas de interface (Executable, WaitState, BoundaryEventDefinition) são usadas pelo motor para decidir
comportamento sem precisar de instanceof em cascata em todo lugar — por exemplo, ProcessExecutionManager
verifica flowNodeDefinition instanceof WaitState para saber se deve suspender a execução (ver
Execução Síncrona e Assíncrona).
:::info Subprocessos: CALL_ACTIVITY_COORDINATOR
CallActivityDefinition ("type": "CALL_ACTIVITY_COORDINATOR" no JSON) inicia uma ou mais instâncias de
outra ProcessDefinition e só segue adiante quando todas concluem — o subprocesso/call activity do
Kikwiflow. Não implementa Executable nem WaitState (o fan-out e a espera são tratados por mecanismo próprio,
não pelo TaskExecutor/WaitState genéricos). Ver o guia dedicado:
CALL_ACTIVITY_COORDINATOR — Subprocessos.
:::
:::info Correlação de eventos: EVENT_CATCHER, BOUNDARY_INTERRUPTIVE_CATCH_EVENT e EVENT_THROWER
Três tipos formam a família de correlação por chave de negócio do Kikwiflow — em vez de um worker puxar
trabalho (fetch-and-lock), um evento externo (webhook, mensagem assíncrona) empurra a chave que destrava a
espera, via KikwiflowEngine.correlateMessage(...):
EVENT_CATCHER(EventCatcherDefinition, implementaWaitState) — nó principal de espera reativa, com modoSTANDALONE(uma chave) ouGROUP(N chaves em scatter-gather). Ver EVENT_CATCHER e EVENT_THROWER no Guia do Desenvolvedor.BOUNDARY_INTERRUPTIVE_CATCH_EVENT(InterruptiveCatchEventDefinition, implementaBoundaryEventDefinition) — o mesmo mecanismo de correlação, mas como evento de borda: cancela o nó pai assim que a chave chega. Ver Boundary Catch Event.EVENT_THROWER(EventThrowerDefinition) — a contraparte de emissão: resolve uma chave e entrega internamente pelo mesmo caminho decorrelateMessage, sem implementarExecutablenemWaitState(o comportamento de lançar é embutido no motor, não delega a umTaskHandler, e não bloqueia quem lançou). Ver EVENT_CATCHER e EVENT_THROWER. :::
:::info Temporizador de fluxo principal: TIMER_TASK
TimerTaskDefinition ("type": "TIMER_TASK" no JSON) pausa a execução até um dueDate calculado
(STATIC/VARIABLE/BEAN, o mesmo mecanismo de InterruptiveTimerEventDefinition) e então segue pelas
próprias arestas de saída — um temporizador que faz parte do fluxo principal, não um evento de borda anexado a
outro nó. Materializado como ExecutableTask (não ExternalTask); não implementa WaitState. Ver
TIMER_TASK no Guia do Desenvolvedor.
:::
Anatomia de um nó EXECUTABLE_TASK
A tarefa executável é o nó que carrega lógica Java síncrona, executada in-process pelo TaskExecutor:
{
"id": "CALCULATE_CUSTOMER_RISK_ST",
"name": "Calcular Risco",
"type": "EXECUTABLE_TASK",
"executor": "calculateCustomerRiskTaskHandler",
"commitBefore": false,
"commitAfter": false,
"outgoing": [ { "targetNodeId": "GATEWAY-CLASSIFICACAO-RISCO" } ]
}
| Campo | Tipo | Descrição |
|---|---|---|
executor | String | Nome do bean Spring TaskHandler a invocar. Ver Visão Geral. |
retryPolicy | RetryPolicy | Política de nova tentativa específica do nó. Ver Retry e Resiliência. |
boundaryEventIds | List<String> | IDs de nós de borda anexados (timers, error handlers). |
commitBefore / commitAfter | Boolean | Fronteiras transacionais. |
Anatomia de um nó EXTERNAL_TASK
Uma tarefa externa não referencia nenhum bean — ela representa trabalho que será concluído fora do processo
Java em execução (um worker externo, uma ação humana via UI, uma integração assíncrona). O motor cria um
registro ExternalTask e aguarda uma chamada explícita a KikwiflowEngine.completeExternalTask(...) (exposta
via REST em PUT /external-tasks/{id}/complete pelo kikwi-management-rest) para prosseguir.
{
"id": "wait-customer-registry",
"name": "Aguardar Cadastro",
"sla": "PT2M",
"type": "EXTERNAL_TASK",
"commitBefore": false,
"commitAfter": false,
"outgoing": [ { "targetNodeId": "do-customer-registry-activation" } ],
"boundaryEvents": [ { "type": "BOUNDARY_INTERRUPTIVE_TIMER", "staticValue": "PT1M" } ]
}
Detalhes de ciclo de vida (claim/unclaim/complete) estão em
Workers e Tarefas Externas.
Anatomia de um nó EVENT_CATCHER
Como EXTERNAL_TASK, também implementa WaitState — mas em vez de ser destravado por taskId via
completeExternalTask, é destravado por uma chave de correlação de negócio via
KikwiflowEngine.correlateMessage(correlationKey, variables, identityContext). O campo catchType decide
entre aguardar uma única chave (STANDALONE) ou N chaves em scatter-gather (GROUP, com matchPolicy: ALL
ou ANY):
{
"id": "WAIT_ORDER_PAID",
"type": "EVENT_CATCHER",
"catchType": "STANDALONE",
"providerType": "VARIABLE",
"providerVariable": "orderId",
"keyPrefix": "ORDER_",
"keySuffix": "_PAID",
"outgoing": [ { "targetNodeId": "prepare-shipment" } ]
}
Cobertura completa (modo GROUP, CorrelationKeysProvider, timers de borda, idempotência) em
EVENT_CATCHER — Correlação de Eventos.
Arestas: SequenceFlowDefinition
Toda aresta de saída (outgoing) de um nó é um SequenceFlowDefinition:
public record SequenceFlowDefinition(
String id, String name, String description,
String expectedAnswer, String targetNodeId,
boolean isDefault, boolean handlesNull,
List<LayoutCoordinates> positionHandlers) {}
Para nós que não são gateways, o motor sempre segue a primeira entrada de outgoing — nós não-gateway
suportam apenas uma saída lógica (embora positionHandlers/layout possam sugerir curvas visuais no editor
gráfico). Os campos expectedAnswer, isDefault e handlesNull só têm efeito em nós EXCLUSIVE_GATEWAY; veja
Navegação e Gateways de Decisão.
Gateways estruturais: PARALLEL_GATEWAY e JOIN_GATEWAY
Diferente do gateway exclusivo, esses dois nós não carregam nenhuma configuração de decisão — são puramente estruturais:
public record ParallelGatewayDefinition(..., String targetJoinId, ...) implements FlowNodeDefinition {}
public record JoinGatewayDefinition(..., String sourceSplitId, ...) implements FlowNodeDefinition {}
ParallelGatewayDefinition.targetJoinIdaponta obrigatoriamente para oJoinGatewayDefinitionque fecha o fan-out — oNavigatorlançaRuntimeExceptionno deploy/execução se esse campo estiver ausente.JoinGatewayDefinition.sourceSplitIdé informativo (documenta de qual split o join se origina), mas a reconciliação real de ramos concluídos é feita viapendingBranchIdsnaExecutableTaskdo tipoJOIN_GATEWAY— coberto em Execução Síncrona e Assíncrona.
Boundary events e attachedToRef
Timers de borda (InterruptiveTimerEventDefinition, NonInterruptiveTimerEventDefinition) e o handler de erro
(ErrorHandlerDefinition) implementam BoundaryEventDefinition, que exige attachedToRef — o id do nó ao
qual o evento está anexado (tipicamente um EXTERNAL_TASK de longa duração ou um EXECUTABLE_TASK):
public sealed interface BoundaryEventDefinition extends FlowNodeDefinition
permits InterruptiveTimerEventDefinition, NonInterruptiveTimerEventDefinition, ErrorHandlerDefinition {
String attachedToRef();
}
No JSON, a associação é declarada duas vezes por razões de navegabilidade do editor: o nó pai lista os boundary
events em boundaryEventIds (ou array embutido boundaryEvents, dependendo de como o exportador estrutura o
documento), e o próprio boundary event referencia attachedToRef de volta para o pai.
Referência rápida de todos os tipos de nó
| Tipo | Interface adicional | Executa lógica Java? | Bloqueia até evento externo? |
|---|---|---|---|
DEFAULT_START_EVENT | — | Não | Não |
DEFAULT_END_EVENT | — | Não | Não |
EXECUTABLE_TASK | Executable | Sim (TaskHandler) | Não |
EXTERNAL_TASK | WaitState | Não (delega a worker externo) | Sim |
EXCLUSIVE_GATEWAY | — | Opcional (AnswerProvider se providerType: BEAN) | Não |
PARALLEL_GATEWAY | — | Não | Não |
JOIN_GATEWAY | — | Não | Sim (até todos os ramos concluírem) |
BOUNDARY_INTERRUPTIVE_TIMER | BoundaryEventDefinition | Opcional (DueDateProvider se providerType: BEAN) | Sim (até o timer disparar) |
BOUNDARY_NON_INTERRUPTIVE_TIMER | BoundaryEventDefinition | Idem | Sim, mas sem interromper o nó pai |
BOUNDARY_ERROR_HANDLER | BoundaryEventDefinition | Não | Não |
EVENT_CATCHER | WaitState | Opcional (CorrelationKeysProvider se providerType: BEAN) | Sim (até correlateMessage com a chave certa) |
BOUNDARY_INTERRUPTIVE_CATCH_EVENT | BoundaryEventDefinition | Opcional (CorrelationKeysProvider se providerType: BEAN) | Sim (até correlateMessage com a chave certa, ou o nó pai terminar antes) |
TIMER_TASK | — | Opcional (DueDateProvider se providerType: BEAN) | Sim (até o timer disparar) |
EVENT_THROWER | — | Não (comportamento embutido no motor, não delega a TaskHandler) | Não (lança e segue — não bloqueia quem lançou) |
CALL_ACTIVITY_COORDINATOR | — | Não (spawna instância(s) filha(s), não roda handler) | Sim (até todas as instâncias filhas concluírem) |