Skip to content

Routing and Fan-out

Routing decides which output branch receives an event. It does not decide how the event crosses the edge, where the consumer runs, or which replica owns an input partition.

Mode Per-event behavior Good fit Does not provide
clone Sends a copy to every named output port Full archive plus independent filtered branches Durability, transactionality across outputs, or deduplication
round-robin Sends each event to one output port in turn Independent stateless consumers Key affinity, partition ownership, ordering across outputs
conditional Maps a static field value to a named port Severity, validity, tenant, event class, or policy routes Dynamic partition discovery or online rebalance
  1. Add the built-in Fan Out or routing primitive between the producer and consumers.
  2. In the Inspector, add one output port for each outcome. Use semantic names such as archive, quarantine, and critical, not out1.
  3. Select the dispatch mode.
  4. For conditional, select the routing field and map every supported value to a port. Define how unmatched or missing values are handled.
  5. Connect each output port to the intended downstream input.
  6. Put branch-specific filters after a clone when one branch must retain the complete valid stream.
  7. Inspect the manifest, validate the graph, and review the generated plan.

A small clone fragment has this shape:

[steps.route.dispatch]
mode = "clone"
ports = ["all", "important"]

Treat the editor-generated manifest as authoritative for the selected release; the fragment illustrates intent and is not a complete workflow.

For full archive plus alerts, clone the validated stream first. Send one port directly to the archive and put the alert predicate on the other port. If the filter runs before the clone, the archive receives only selected events.

flowchart LR
  accTitle: Loss-aware archive and alert fan-out
  accDescr: Input is validated before fan-out. Invalid events go to quarantine. Every valid event is cloned to the archive, while a separate copy passes through the alert predicate and only matching events reach the selected branch.
  input["Input events"] --> valid{"Valid?"}
  valid -- No --> quarantine["Quarantine"]
  valid -- Yes --> clone{{"Clone valid stream"}}
  clone --> archive["Archive all valid events"]
  clone --> filter["Apply alert predicate"]
  filter --> selected["Selected alert branch"]

Clone intentionally duplicates processing. Verify:

valid input = archive output
selected predicate count = selected branch output
quarantine + valid input = total input
unexplained loss = 0
unexpected duplicates = 0

Round-robin distributes events among output ports but does not keep all events for a key together and does not coordinate a broker’s consumer group. Use it only when consumers are independent and stateless or when ordering/key affinity is irrelevant. For horizontally scaled sources, prove the source or broker’s ownership contract separately.

Conditional routing is excellent for severity=critical, valid=false, or a known tenant list. Add a deliberate fallback—often quarantine—so new values do not disappear. A conditional map does not discover new partitions or rebalance consumers at runtime.

Multiple data producers may target a compatible downstream transport, but schema, ordering, and duplicate behavior remain transport- and consumer-specific. Message edges are different: they carry wakeup signals, not bulk data, and multiple incoming messages are OR-style triggers. Model an AND join with explicit durable state and a supported join pattern.

After routing, choose Transports and Durability, then Placement and Scaling. Finish by defining runtime and destination evidence.

Practice this with Tutorial: Local Fan-out.