Pular para o conteúdo principal
Página não listada
Esta página não está listada. Mecanismos de busca não irão indexá-la, e somente usuários que possuam o link direto poderão acessá-la

CALL_ACTIVITY_COORDINATOR — Subprocessos

Pense num gerente que delega. Ele recebe "processar 30 contratos", passa cada contrato para um analista diferente, e só reporta "concluído" para cima quando todos os 30 devolverem o resultado. Cada analista trabalha sozinho, com sua própria mesa e seus próprios problemas — se um deles trava, o gerente continua esperando aquele, mas os outros 29 seguem. Onde a analogia quebra: o gerente humano pode decidir "esse contrato fica para depois, sigo com o resto"; a v1 do Kikwiflow ainda não tem essa política de "pular o item com falha".

Um CALL_ACTIVITY_COORDINATOR inicia uma ou mais instâncias de outra ProcessDefinition — referenciada por chave (calledElement), não por um handler Java — e só segue adiante quando todas concluem. Cada filho roda na sua própria ProcessInstance: ciclo de vida, incidentes e histórico isolados do pai. É o subprocesso / call activity do Kikwiflow.

No fio de abertura de conta, o encaixe é a ativação de produtos: depois que a conta está ativa, cada produto que o cliente contratou (produtos) é ativado por um subprocesso próprio, isolado — se a ativação de um trava, os outros seguem.

Referência de campos

CampoObrigatório?Descrição
idSimIdentificador único do nó.
name / descriptionNãoRótulo e texto livre.
calledElementSimkey da ProcessDefinition a iniciar. Não validada contra processos já implantados no momento da implantação.
collectionVariableNãoAusente → 1 filho. Presente → 1 por elemento da lista (precisa resolver para uma List em tempo de execução).
elementVariableSó se collectionVariable presenteNome sob o qual o elemento da vez entra nas variáveis iniciais de cada filho.
iterationModeNão (default PARALLEL)PARALLEL: todas as N filhas disparam de uma vez. SEQUENTIAL: uma de cada vez — a próxima só inicia depois que a anterior concluir.
boundaryEventIdsNãoIDs de BOUNDARY_INTERRUPTIVE_TIMER/BOUNDARY_NON_INTERRUPTIVE_TIMER. Não aceita BOUNDARY_INTERRUPTIVE_CATCH_EVENT nem BOUNDARY_ERROR_HANDLER.
outgoingSimPara onde seguir quando todas as filhas concluírem.

commitBefore/commitAfter não têm efeito — este nó é sempre tratado como assíncrono.

Um único filho (sem collectionVariable)

{
"id": "CHAMAR_VERIFICACAO_KYC",
"name": "Verificar KYC do Cliente",
"type": "CALL_ACTIVITY_COORDINATOR",
"calledElement": "verificacao-kyc",
"outgoing": [ { "targetNodeId": "CALCULAR_SCORE" } ]
}

O motor inicia uma instância de verificacao-kyc com todas as variáveis do processo pai como variáveis iniciais do filho, e aguarda ela chegar em COMPLETED antes de seguir.

N filhos em paralelo (collectionVariable + elementVariable)

{
"id": "PROCESSAR_DOCUMENTOS",
"name": "Processar Documentos Anexados",
"type": "CALL_ACTIVITY_COORDINATOR",
"calledElement": "processamento-documento",
"collectionVariable": "documentos",
"elementVariable": "documento",
"outgoing": [ { "targetNodeId": "CALCULAR_SCORE" } ]
}

Se documentos = ["rg", "comprovante-residencia", "selfie"], o motor inicia três instâncias de processamento-documento em paralelo — cada uma recebendo todas as variáveis do pai mais documento = o elemento daquela vez. O fluxo só segue quando os três concluírem, em qualquer ordem.

A lista é lida uma única vez, ao alcançar o coordenador — mudar a variável depois não afeta os filhos já iniciados. Lista vazia: o fluxo segue direto, sem iniciar nenhum filho.

N filhos em sequência (iterationMode: SEQUENTIAL)

{
"id": "ATIVAR_PRODUTOS",
"type": "CALL_ACTIVITY_COORDINATOR",
"calledElement": "ativacao-produto",
"collectionVariable": "produtos",
"elementVariable": "produto",
"iterationMode": "SEQUENTIAL",
"outgoing": [ { "targetNodeId": "CONFIRMAR_PACOTE" } ]
}

Com produtos = ["conta-corrente", "cartao-credito", "seguro-vida"], o motor inicia uma instância por vez — cartao-credito só começa depois que conta-corrente chegar em COMPLETED.

Use SEQUENTIAL quando os itens têm dependência de ordem real (um cartão vinculado a uma conta-corrente que precisa existir primeiro; uma etapa regulatória que só pode começar depois da anterior ser auditada). Se são independentes, prefira PARALLEL (padrão) — é estritamente mais rápido.

Uma falha num elemento atrasa os seguintes, não os pula. Se o filho do elemento 2 fica preso num incidente, o elemento 3 nunca inicia — a sequência trava esperando o 2 se resolver. Não há (ainda) política de "pular o item com falha".

O processo filho (ativacao-produto)

O calledElement aponta para uma ProcessDefinition normal, com seu próprio .kikwi e seus próprios beans — nada a mais. O ativacao-produto do exemplo é minúsculo:

DEFAULT_START_EVENT
→ EXECUTABLE_TASK "Criar Produto" (executor: criarProdutoTaskHandler,
commitBefore: true, retryPolicy LINEAR)
→ EXECUTABLE_TASK "Notificar Cliente" (executor: notificarClienteTaskHandler)
→ DEFAULT_END_EVENT "Produto Ativado"

Cada instância filha recebe todas as variáveis do pai mais produto (o item da vez, no nome declarado em elementVariable). O commitBefore: true em Criar Produto garante que, se a aplicação reiniciar no meio da criação, aquele filho é retomado do começo — sem afetar os irmãos.

Timeout (boundary event)

Um timer de borda anexado ao coordenador cancela a espera inteira:

{
"id": "TIMEOUT_ATIVACAO",
"type": "BOUNDARY_INTERRUPTIVE_TIMER",
"attachedToRef": "ATIVAR_PRODUTOS",
"providerType": "STATIC",
"staticValue": "PT2M",
"outgoing": [ { "targetNodeId": "NOTIFICAR_ATIVACAO_PARCIAL" } ]
}

Ao disparar, a coordenadora e qualquer iniciadora ainda pendente são apagadas, e o fluxo do pai segue por NOTIFICAR_ATIVACAO_PARCIAL. Um filho já iniciado é cancelado junto — o motor apaga a ProcessInstance do filho e suas tarefas na mesma transação, gravando um evento crítico PROCESS_INSTANCE_FINISHED com status: CANCELLED no outbox para rastreabilidade (ver Eventos e Observabilidade). Isso é recursivo: se o filho também tem uma call activity que gerou um neto, o neto é cancelado junto — a subárvore inteira some. Ver Timers e Prazos para as opções de providerType do timer.

Validações

No momento da implantação (DeployValidator): calledElement nulo/vazio → InvalidProcessDefinitionException; elementVariable presente sem collectionVariableInvalidProcessDefinitionException. calledElement não é validado contra processos já implantados (cada processo é implantado de forma independente e podem chegar em qualquer ordem).

Em tempo de execução, ao alcançar o coordenador: collectionVariable que aponta para variável ausente, nula ou que não é lista → falha isolada à tarefa que disparou a chegada (retentativa/incidente normal); calledElement que não resolve → falha isolada àquele filho especificamente (abre um incidente vinculado só a ele, retentável via PUT /incidents/{id}/retry).

:::info Por que a validação de lista não acontece na implantação O motor não tem declaração de tipo para variáveis — não existe "schema" de variáveis por ProcessDefinition. O tipo de uma variável só existe quando ela tem um valor, em tempo de execução. Por isso "collectionVariable precisa ser uma lista" só pode ser checado ao alcançar o coordenador. :::

Isolamento de falha

Falha dentro do processo filho (um incidente aberto lá, o filho nunca chega a COMPLETED) deixa o coordenador esperando indefinidamente — a não ser que haja um timeout configurado. Não existe (ainda) um canal de erro dedicado para propagar a falha de volta ao pai automaticamente. Trate o incidente diretamente no filho (ele é uma ProcessInstance normal, com parentInstanceId apontando para o pai) ou configure um boundaryEventIds.

Forma em tempo de execução (resumo)

Alcançar o coordenador cria, na mesma transação e sem iniciar nenhum filho ainda:

  • 1 ExecutableTask do tipo CALL_ACTIVITY_COORDINATOR — a espera em si; pendingBranchIds encolhe a cada filho que conclui.
  • iniciadoras (CALL_ACTIVITY_STARTER), uma por filho: em PARALLEL, N de uma vez; em SEQUENTIAL, só a do elemento corrente (as demais são criadas sob demanda). Cada iniciadora chama startProcess() para criar a instância filha e então se auto-apaga.

Cada filho conclui liberando automaticamente sua "vaga" no coordenador — pelo mesmo mecanismo que sincroniza ramos de um PARALLEL_GATEWAY, atravessando a fronteira entre instâncias.

Quando usar / quando não usar

SituaçãoUse
O passo seguinte é executar outro processo completo (ciclo de vida, incidentes e histórico próprios); ou abrir vários ramos a partir de uma lista de tamanho só conhecido em tempo de execuçãoCALL_ACTIVITY_COORDINATOR (PARALLEL se itens independentes; SEQUENTIAL se há ordem real)
O número de ramos é fixo e conhecido ao desenhar o processo (sempre 2, sempre 3)PARALLEL_GATEWAY
O que se espera são eventos externos (webhooks, callbacks), não subprocessosEVENT_CATCHER modo GROUP

Não use para lista com dependência de ordem em modo PARALLEL — um item pode começar antes do seu pré-requisito. Não conte com propagação automática de erro filho→pai: configure timeout ou monitore os incidentes dos filhos.

Fora de escopo desta versão

  • Mapeamento explícito de variáveis de entrada/saída — hoje todas as variáveis do pai são propagadas para cada filho; não há como enviar só um subconjunto nem escolher o que volta.
  • Canal de erro dedicado filho → pai — um incidente no filho não é refletido no pai além de deixá-lo esperando.
  • Limite de tamanho de lista (circuit breaker) — não há teto configurável para collectionVariable.

Próximo passo

Continue para Documentação Markdown no Diagrama.