Controle de fluxo
branch/switch/foreach/parallel/try/group compõem lógica condicional e iterativa sem
sair do YAML.
when: o caminho barato
Todo step aceita três campos universais:
| Campo | Efeito |
|---|---|
type |
Obrigatório. A chave que resolve a implementação |
id |
Opcional. Nomeia o step. Só publica em steps.<id>.result nos steps que produzem resultado — ver abaixo |
when |
Opcional. Expressão booleana; o step só executa se verdadeira |
id: não é um mecanismo universal de captura. Declará-lo num step que não produz resultado
(map, set, log, publish, validate, fail, return, db.persist/update/delete/sql,
cache.set, cache.invalidate, storage.delete) é aceito no build e simplesmente não publica
nada — nenhum erro avisa. Publicam em steps.<id>.result: db.find, db.exists, db.query,
cassandra.query, dynamo.query, cache.get, http.call, grpc.call e os storage.* de leitura
e escrita. A forma que cada um publica está na
referência de steps.
when é mais barato que branch para o caso comum de pular um único step:
- { type: db.find, id: found, entity: widget, where: { id: "input.route.id" } }
- { type: fail, when: "steps.found.result = null", status: 404, code: WIDGET_NOT_FOUND }
- type: return
Use branch/switch quando o que muda é qual grupo de steps roda, não se um único step roda.
Os steps de controle de fluxo
| Step | Função |
|---|---|
branch |
when (campo do próprio step, seleciona entre os dois ramos) / then / else |
switch |
cases: [{ when, then }], mais default |
foreach |
in (expressão), steps, parallelism opcional (default 1), collect: { to, select } |
parallel |
branches: [[...], [...]] — sub-pipelines independentes, executados concorrentemente |
try |
try / catch opcional / finally opcional — a falha fica disponível em error dentro de catch |
group |
Agrupa steps só para legibilidade/organização, sem when próprio nem outro efeito |
branch é o único step com when como campo do próprio bloco — ele escolhe entre then e else.
Todo outro step usa o when universal descrito acima.
- type: switch
cases:
- { when: "input.body.type = 'premium'", then: [ { type: map, to: vars.discount, expr: "0.2" } ] }
- { when: "input.body.type = 'standard'", then: [ { type: map, to: vars.discount, expr: "0.1" } ] }
default: [ { type: map, to: vars.discount, expr: "0" } ]
- type: foreach
in: "input.body.items"
steps:
- { type: map, to: vars.line, expr: "{ 'sku': item.sku, 'total': item.price * item.quantity }" }
collect:
to: vars.lines
select: vars.line
- type: try
try:
- { type: http.call, integration: pricing, method: POST, path: /quote }
catch:
- { type: log, message: "falha ao consultar preço, seguindo com o fallback" }
- { type: map, to: vars.price, expr: "0" }
Dentro de um foreach, o elemento da iteração é item e a posição é index — campos comuns do
estado, não variáveis JSONata. Escrever $item (com cifrão) é o erro mais fácil de cometer aqui, e
ele falha em silêncio: $item é uma variável JSONata não definida, avalia para vazio, e o
foreach termina sem erro produzindo resultados vazios. item/index também estão disponíveis na
expressão de collect.select.
No log, message e os valores de fields são literais — não são avaliados como expressão.
message: "'erro: ' & error.message" não interpola nada: registra o texto JSONata cru, exatamente
como escrito.
Escopo de id e de vars
Cada bloco composto (then/else/cases[].then/default/foreach.steps/parallel.branches/
try/catch/finally) tem seu próprio escopo de ids — uma referência steps.<id> de fora
desse bloco falha o build, não silenciosamente em runtime.
vars, ao contrário, não é escopado por bloco: uma escrita feita dentro de um
foreach/try/etc. permanece visível depois dele.
Um detalhe que morde: transação e concorrência
Não misture db.* concorrente com transaction: required. Um parallel com mais de um ramo
fazendo db.*, ou um foreach com parallelism > 1 chamando db.*, dentro de uma rota
transacional, colide — a sessão transacional é uma única conexão, não é seguro usá-la
concorrentemente.
Duas formas seguras de evitar isso:
- Pareie no máximo um ramo de
db.*com computação pura dentro deparallel(o outro ramo não toca o banco). - Rode o
foreachsequencial (parallelism: 1, que já é o valor padrão) sempre que ele fazdb.*dentro de uma rota transacional.
Para onde ir daqui
- Autenticação e autorização
- Integrações outbound —
try/catché frequentemente combinado comhttp.call/grpc.callpara tratar falhas de um sistema externo.