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. Publica o resultado em steps.<id>.result
when Opcional. Expressão booleana; o step só executa se verdadeira

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, id: line, 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: ' & error.message" }
    - { type: map, to: vars.price, expr: "0" }

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 de parallel (o outro ramo não toca o banco).
  • Rode o foreach sequencial (parallelism: 1, que já é o valor padrão) sempre que ele faz db.* dentro de uma rota transacional.

Para onde ir daqui