Skip to content

Build1 publisher3 min readPublished

The sub-second saga in this BIAN design tolerates 26.5 seconds before it compensates

A dual-orchestration pattern hands machine sagas to Temporal and human approvals to Flowable along BIAN service-domain lines. The retry policy in its Go sample shows what the machine side's latency budget really allows.

The Engineer · Build desk

Illustration accompanying The sub-second saga in this BIAN design tolerates 26.5 seconds before it compensates

What happened

  • A dev.to post by Mountek argues that one orchestrator cannot serve both execution profiles, because BPMN engines pushed to run low-latency microservice Sagas hit database state bloat and worker thread starvation.
  • The reverse pairing fares no better in the post's account: code-first workflow engines running multi-week human tasks obscure business visibility, hardcode approval chains and compromise audit compliance.
  • Its answer, under what it calls the Xenon Architecture Standards, is a dual-orchestration pattern mapped to BIAN service domains, with Temporal on the machine Sagas and Flowable on the human-in-the-loop processes.
  • Distributed ACID across Position Keeping, Risk Management and General Ledger is ruled out for lock contention, so Temporal registers an explicit compensating action for every forward step instead.
  • Temporal is described as persisting the complete stack execution trace, so execution survives process crashes, network partitions and downstream infrastructure outages.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • contradiction The machine side is sold as sub-second, and its own activity options permit a single step to run roughly 26.5 seconds before compensation, so downstream reservation and limit-check timeouts have to be sized for the retry tail.
  • decision Because the three assignment axes describe process instances, a bank adopting this has to decide engine ownership process by process; any domain holding both a sub-second posting and a human clearance step gets split or gets the wrong engine.
  • cost The pattern buys two orchestrators with two upgrade cycles and one asynchronous protocol between them, and the platform team that operates the hop pays for its idempotency and replay behaviour.

Take the activity options in the Go sample literally. StartToCloseTimeout is 5 seconds, InitialInterval is 100 milliseconds, BackoffCoefficient is 2.0, MaximumAttempts is 5 [12]. The waits between attempts run 100, 200, 400 and 800 milliseconds, which is 1.5 seconds, and five attempts that each burn the full timeout add 25 seconds. One step of the payment saga can therefore sit for about 26.5 seconds before it fails and the compensations begin [1]. The post describes that execution profile as deterministic, programmatic and sub-second [10]. That is the median case. Whoever sets the reservation timeout in Position Keeping has to size it for the other one [11].

The assignment rule is the strongest part of the paper, and it does not actually run along domain lines. Orchestration responsibility is allocated by state duration, execution throughput, and the presence of human intervention [7]. Those are properties of a process instance. BIAN service domains are units of encapsulation that expose functional capability through control keys and service operations, and the post says execution profiles vary drastically across them [6]. The two only coincide when every process inside a domain shares one profile. The diagram puts the Screening Engine under Temporal [8]. If a screening hit needs a compliance officer to clear it, a multi-day human task is sitting inside a domain the design has already handed to the machine engine.

The hard part of a two-engine design is the arrow between them. The diagram labels it async gRPC or an event bus [8], and the published excerpt stops inside the first compensation registration [16], so neither the Flowable process definition nor the code that crosses the hop appears. Those are the interesting failures: an approval that arrives after the saga has already compensated, a saga retry that creates a second human task, an event replayed into a BPMN receive task that has already fired. The payload carries a CorrelationID field [14]. I would key idempotency on it at both ends of the hop, or on the BIAN control key, and I would want that decided in the contract before either engine is chosen.

The compensation path itself is well built. Failures unwind in reverse order on a disconnected context [13], so the rollback still runs when the parent workflow context is cancelled. That is the behaviour you want when a downstream outage kills the forward path, which is the class of fault the durable execution model is sold on [9]. One field I would change before shipping: Amount is typed float64 [14]. A reserve step and a general ledger posting have to agree on the exact minor unit.

I would take this split where the two edit cycles differ, which in a bank they usually do: a compliance team changes an approval chain in a modelled diagram, engineers change the payment path in a deploy [4]. The cost is two engines to upgrade and one hop that both sides have to treat as unreliable. The post argues the case on structure, and the argument stands or falls on the seam it has not published yet.

What to watch

  • A follow-up that publishes the failure-pattern section for the gRPC or event-bus hop, in particular what happens when an approval lands after the Saga has compensated.
  • Any measured figures behind the state-bloat claim: BPMN job executor thread pool size and the Saga arrival rate at which the engine starves.
  • Whether BIAN control keys or the CorrelationID field are made to carry idempotency across the seam.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories