Timers e Prazos
Timers modelam a passagem do tempo dentro de um processo: uma aprovação que expira, um SLA que
dispara um alerta, uma escalada automática. São sempre declarados como eventos de borda
(boundary events), anexados a um nó via attachedToRef — tipicamente uma EXTERNAL_TASK. Um
EXECUTABLE_TASK aceita só o timer não-interruptivo: seu handler roda de forma síncrona e não há como
interrompê-lo com segurança no meio do caminho. A matriz completa de quem aceita qual evento de borda está
em Referência de Nós e Linter.
Um boleto fica válido até uma data de vencimento; passado esse prazo, ele simplesmente deixa de valer e o processo de cobrança segue por outro caminho — sem que ninguém precise ficar checando o relógio manualmente. A analogia cobre bem o timer interruptivo (o vencimento desvia o fluxo). Ela não cobre o timer não-interruptivo: um boleto não manda lembretes periódicos sozinho, mas o Kikwiflow pode disparar alertas repetidos — "faltam 3 dias", "faltam 3 horas" — sem invalidar nada, em paralelo à espera original.
Existem dois tipos, com efeitos bem diferentes sobre o nó ao qual estão anexados:
| Tipo | Dispara | Efeito sobre o nó pai |
|---|---|---|
BOUNDARY_INTERRUPTIVE_TIMER | Uma vez, em um instante calculado | Interrompe o nó pai — a espera original é cancelada e a execução desvia para a saída do timer |
BOUNDARY_NON_INTERRUPTIVE_TIMER | Repetidamente, segundo uma política de agendamento | Não interrompe o nó pai — dispara em paralelo (ex.: notificações periódicas de SLA) |
"E se o analista não decidir em 48h?"
Num processo de abertura de conta, a tarefa externa Aprovar Abertura espera indefinidamente por
padrão. Um timer interruptivo dá um prazo real a essa espera: se o analista de crédito não registrar a
decisao em 48 horas, a solicitação é recusada
automaticamente por SLA e o fluxo segue para o encerramento, em vez de ficar pendurado para
sempre.
{
"id": "timer-sla-aprovacao",
"type": "BOUNDARY_INTERRUPTIVE_TIMER",
"providerType": "STATIC",
"staticValue": "PT48H",
"attachedToRef": "APROVAR_ABERTURA",
"outgoing": [ { "targetNodeId": "END_RECUSADA_POR_SLA" } ]
}
O prazo-alvo é resolvido por uma entre três estratégias, via providerType:
providerType | Origem do valor | Campo relevante |
|---|---|---|
STATIC | Valor fixo no JSON | staticValue |
VARIABLE | Variável de processo já calculada | providerVariable |
BEAN | Bean Spring implementando DueDateProvider | providerBean |
DueDateProvider: prazos calculados em Java
Quando o prazo depende de uma regra de negócio — por exemplo, um cliente VIP tem um SLA de análise mais curto que os demais:
public interface DueDateProvider {
/** ISO-8601: duração ("PT1H") ou data absoluta ("2026-12-25T20:00:00Z") */
String resolve(EvaluationContext execution);
}
package com.empresa.processo.decision;
import io.kikwiflow.execution.api.context.EvaluationContext;
import io.kikwiflow.execution.api.provider.DueDateProvider;
import org.springframework.stereotype.Component;
@Component("slaAprovacaoBean")
public class SlaAprovacaoDueDateProvider implements DueDateProvider {
@Override
public String resolve(EvaluationContext execution) {
boolean vip = Boolean.TRUE.equals(execution.getVariableValue("clienteVip").orElse(false));
return vip ? "PT12H" : "PT48H";
}
}
O valor retornado — de staticValue, de uma variável, ou de um DueDateProvider — aceita
indistintamente uma duração ISO-8601 ("PT48H", somada ao instante atual) ou uma data
absoluta ISO-8601 ("2026-12-25T20:00:00Z"). Não é preciso declarar qual formato está sendo
usado; o motor tenta interpretar como data absoluta primeiro e, se falhar, como duração relativa.
Timer não-interruptivo: alertas periódicos
Use quando o timer só precisa produzir um sinal em paralelo, recorrentemente, sem afetar a tarefa original — por exemplo, lembrar o analista de crédito a cada 12 horas enquanto Aprovar Abertura segue pendente:
{
"id": "lembrete-aprovacao",
"type": "BOUNDARY_NON_INTERRUPTIVE_TIMER",
"attachedToRef": "APROVAR_ABERTURA",
"schedulePolicy": { "type": "RATE_DURATION", "expression": "PT12H" }
}
schedulePolicy.type aceita RATE_DURATION (repete a cada duração ISO-8601 declarada em
expression, indefinidamente) ou FIXED_DATES (uma lista de instantes ISO-8601 em fixedDates;
quando o último já passou, o ciclo termina naturalmente, sem reagendar).
CRONschedulePolicy.type aceita apenas RATE_DURATION e FIXED_DATES — expressões cron (* * * * *) não são
suportadas, e "type": "CRON" é rejeitado no deploy.
Declarar no Craft
No Craft, selecione o nó Aprovar Abertura e arraste da paleta de eventos de borda um timer
interruptivo (SLA timeout) para a borda dele; ligue a saída do timer ao encerramento por SLA. Um timer
não-interruptivo para o lembrete convive no mesmo nó sem conflito. O Craft escreve o attachedToRef
automaticamente. Só o modo providerType: BEAN exige código — a classe DueDateProvider acima, um
@Component nomeado igual ao providerBean.
Quando usar / quando não usar
Use interruptivo quando o vencimento do prazo deve desviar o fluxo para outro caminho — como recusar por SLA uma aprovação parada há 48h.
Use não-interruptivo quando o vencimento apenas dispara um efeito colateral (notificação, alerta), sem alterar o caminho da tarefa original — como lembrar o analista a cada 12h.
Não use um timer para esperar por um evento externo com uma chave de negócio conhecida (um webhook, uma confirmação de outro sistema) — isso é correlação de eventos, não um prazo.
Próximo passo
- Quando o prazo é o próximo passo do caminho feliz ("aguardar 24h antes de seguir"), e não um evento de borda, veja Timer Task.
- Para modelar explicitamente os erros de negócio que um passo pode sinalizar, veja Tratando Erros de Negócio.