Skip to main content

Online edition for BLOGE 0.9.8-RC1 · facts verified 2026-09-15 · 中文

Chapter 33 — Migration and Comparison

Promise: By the end of this chapter you will be able to state constraints before choosing BLOGE, name at least three cases where BLOGE should not be chosen, and complete a short ADR with evidence, a counterfactual, and a first migration probe.


Learning Goals

  1. Restate a tool-selection question as deployment, ownership, recovery, verification-governance, and ecosystem constraints.
  2. Eliminate unsuitable candidates with hard stops instead of totaling feature check marks.
  3. Separate “the feature exists,” “the team can operate it,” and “the evidence qualifies a release.”
  4. Design a reversible first migration step instead of using a full rewrite to test the choice.
  5. Write a short ADR containing the choice, evidence, counterfactual, migration probe, and exit criteria.

Prerequisites

Source Examples

FileWhat it shows
BpmnToDslExample.javaEnd-to-end: translate BPMN XML → .bloge DSL → compile → execute
bloge-bpmn-transformer/README.mdTranslation pipeline, supported scope, diagnostics, and known limitations
order-process.blogeCanonical order-processing graph — parallel fetch, branch, resilience
OrderProcessingDslExample.javaDSL compilation, registry wiring, and execution from Java
order-saga.blogeSaga-style compensation: compensate on each node
payment-wait.blogeLong-running await with event correlation and timeout
batch-order-processing.blogeforeach parallel fan-out over a collection
SpringTicketTriageService.javaInjecting GraphEngine and List<Graph> into a Spring service
ADR-001Core zero-dependency decision
ADR-002Virtual threads over CompletableFuture
ADR-004HCL-inspired DSL over YAML/JSON

Why This Matters

You have spent sixteen chapters learning how to build, test, and run BLOGE workflows. Now a harder question arrives: should you use BLOGE for this particular project?

Teams rarely start with a blank slate. They have:

  • Existing service methods with embedded orchestration logic.
  • Spring Batch jobs handling step-oriented batch processing.
  • BPMN diagrams authored in a visual modeller.
  • Or they are evaluating workflow-as-code platforms that require a separate server cluster.

This chapter gives you a structured way to compare what BLOGE actually does against what those alternatives actually do — grounded in architectural properties you can point to in the repository, not marketing claims.


Mental Model

Think of orchestration tools as sitting on two axes:

Diagram: 33-migration-and-comparison figure 1

Vertical axis — workflow shape visibility. The top half captures the workflow structure in a model (XML, DSL, or visual graph). The bottom half embeds structure entirely in code.

Horizontal axis — deployment topology. The left requires a separate server cluster. The right embeds into your existing application process.

BLOGE sits in the upper-right quadrant: a declarative DAG model that runs as a library inside your JVM, with no external infrastructure required by default.

This is not universally best — it is a specific set of trade-offs:

PropertyBLOGE's choiceWhere it is documented
Zero external dependencies in corebloge-core depends on java.base aloneADR-001
Virtual-thread executionEach node runs in its own virtual threadADR-002
HCL-inspired DSLBlock-oriented, expression-capable, comment-friendlyADR-004
Checkpoint-based durabilityOpt-in via bloge-durable; stores are pluggableChapter 13, README
Embeddable engineGraphEngine is a stateless library objectChapter 21
Resilience per nodeRetry → Timeout → Fallback chain, per nodeADR-007

Three orchestration modules expand the platform beyond graph-only orchestration:

ModuleWhat it adds
bloge-agent-extLLM-driven agent loops with agent DSL, tool dispatch, memory strategies, and streaming
bloge-session-durableDurableSessionManager — crash-safe multi-turn sessions with checkpoint persistence
bloge-state-durableDurableStateMachineManager — crash-safe state machines with checkpoint persistence

Constraints Before Comparisons

The loan platform has just completed business-correctness verification. The architecture board is not asking “how many features does BLOGE have?” It is asking: under the project's non-negotiable constraints, can BLOGE become an operable, verifiable, and reversible choice?

Diagram: a constraint-first selection funnel

Write hard constraints first, evaluate preferences second, and design a migration probe last. A candidate that violates a hard constraint stops; other feature scores cannot average the conflict away.

ConstraintLoan-platform questionBLOGE factDecision meaning
Deployment topologyCan the deployment add a workflow cluster?Engine embeds in the JVM; durable stores are optionalStill a candidate when new clusters are forbidden
Model ownershipWho reviews and changes the flow?.bloge is an explicit, code-friendly DAGA conflict when the business must own BPMN directly
Recovery semanticsDoes recovery require checkpoints or full history replay?BLOGE checkpoints; it is not a general history-replay platformStop when replay is mandatory
Verification governanceMust releases bind approved Scenarios, evidence, and source identity?RC1 provides Scenario/Policy/Fixture, source-bound evidence, and claimsSupports the gate, but still needs organisational owners
Ecosystem boundaryMust workers run natively in several languages?The primary runtime is JVM-basedStop when polyglot workers are mandatory

Diagram: choose BLOGE or stop

This matrix does not produce a fake score such as “BLOGE: 82.” It produces STOP, PROBE, or ADOPT. PROBE means a decisive fact is unknown and must be measured; it is not a polite pass.

Three Clear Reasons Not to Choose BLOGE

  1. A non-development team directly owns BPMN models. If visual collaboration, human-task inboxes, and the existing process server are organisational contracts, migration breaks ownership; it does not merely replace syntax.
  2. The recovery contract requires complete history replay. When audit or disaster recovery must reconstruct every step from event history, checkpoint recovery cannot substitute for replay merely because both can resume work.
  3. Polyglot workers are a hard constraint. If one orchestration must natively dispatch Go, Python, and TypeScript workers, an embedded JVM model transfers the adapter cost to the team.

There is also a simpler stop: when the flow is three sequential calls with no waiting, branching, independent recovery, or shared governance need, a normal method is cheaper. Architectural capability must pay for itself by reducing real complexity.

A Completed Loan-Platform ADR

The loan platform forbids a new standalone cluster, runs on the JVM, and requires every release to bind approved Scenarios to source evidence. Full history replay and polyglot workers are not current requirements. The decision is therefore PROBE BLOGE, not immediate fleet-wide adoption.

ADR fieldThis decision
ChoiceRun a two-week reversible probe on the auto-approve path
EvidenceJVM embedding, explicit DAGs, RC1 verification, and source-bound evidence meet hard constraints
CounterfactualIf history replay or non-JVM workers become mandatory, choose a server-based workflow platform
First migration stepMove read-only risk checks into operators; leave the disbursement effect on the old path
Exit criteriaStop expansion if the graph contract, business Scenario, or recovery drill fails

The ADR does not “approve BLOGE.” It turns the next step into a falsifiable experiment. A green graph test cannot replace a business Scenario, and a PASS cannot replace source binding and release governance.


First Working Example

The most common migration scenario is replacing a hand-written Java orchestration method with a BLOGE graph. The migration below proceeds step by step.

Before: hand-written glue code

// Typical procedural orchestration buried in a service method
public OrderResult processOrder(String userId, List<String> productIds) {
// 1. Sequential — but these two calls are independent
User user = userService.fetchUser(userId);
List<Product> products = productService.fetchProducts(productIds);

// 2. Retry logic tangled with business code
BigDecimal total = null;
for (int attempt = 0; attempt < 3; attempt++) {
try {
total = pricingService.calculate(user, products);
break;
} catch (Exception e) {
if (attempt == 2) throw e;
Thread.sleep(100 * (attempt + 1));
}
}

// 3. Branch logic buried in an if-statement
CreditResult credit = creditService.check(user.id(), total);
if (credit.approved()) {
return orderService.create(user, total);
} else {
return orderService.reject(user.id(), credit.reason());
}
}

Problems:

  • Steps 1 and 2 are independent but execute sequentially.
  • Retry logic is hand-rolled and mixed into the business method.
  • No one can see the workflow shape without reading every line.

After: BLOGE graph

The same workflow, expressed as order-process.bloge:

The 46-line graph stays whole because the comparison needs one copyable unit that exposes parallel fetch, fan-in, resilience, and both branch targets.

graph orderProcess {

node fetchUser : FetchUserOperator {
input { userId = ctx.userId }
timeout = 3s
retry = { attempts: 2, backoff: 200ms, strategy: exponential }
}

node fetchProducts : FetchProductsOperator {
input { productIds = ctx.productIds }
timeout = 5s
}

node calcPrice : CalcPriceOperator {
depends_on = [fetchUser, fetchProducts]
input {
user = fetchUser.output
products = fetchProducts.output
}
}

node checkCredit : CreditCheckOperator {
depends_on = [fetchUser, calcPrice]
input {
userId = fetchUser.output.id
amount = calcPrice.output.total
}
retry = { attempts: 3, backoff: 100ms, strategy: jitter }
fallback = { approved: false, reason: "credit service unavailable" }
}

branch on checkCredit.output.approved {
true -> createOrder
false -> rejectOrder
}

node createOrder : CreateOrderOperator {
depends_on = [calcPrice]
input { user = fetchUser.output, price = calcPrice.output }
}

node rejectOrder : RejectOrderOperator {
depends_on = [checkCredit]
input { userId = fetchUser.output.id, reason = checkCredit.output.reason }
}
}

What changed:

  • fetchUser and fetchProducts run in parallel — the engine sees they have no dependency on each other.
  • Retry and timeout are declared on the node, not coded in a loop.
  • The branch is a first-class routing decision, not a hidden if.
  • The shape is visible, reviewable, and toolable.

The same registry/engine wiring looks like this when the DSL lives in a file:

var registry = new DefaultOperatorRegistry();
registry.register("FetchUserOperator", fetchUserOp);
registry.register("FetchProductsOperator", fetchProductsOp);
// … register remaining operators …

Graph graph = new GraphLoader(registry).load(
Path.of("bloge-examples/src/main/resources/bloge/order-process.bloge"));
GraphEngine engine = GraphEngine.builder().registry(registry).build();
GraphResult result = engine.execute(graph, new GraphContext(Map.of(
"userId", "user-42",
"productIds", List.of("prod-1", "prod-2", "prod-3")
)));

Break It Apart

BLOGE vs hand-written orchestration

DimensionHand-written glue codeBLOGE graph
ConcurrencyYou manage threads, pools, futuresEngine schedules virtual threads from the DAG shape (ADR-002)
ResilienceHand-rolled retry loops, try/catch fallbacksDeclared per node: retry, timeout, fallback (ADR-007)
VisibilityRead the code; hope the comments are accurateThe .bloge file is the documentation; Studio renders it visually
TestabilityMock every service call in each testMockOperator, GraphTestRunner, DslTestHelper from bloge-test
DurabilityBuild your own checkpoint/recoveryOpt-in via bloge-durable stores (Chapter 13)

When hand-written code is fine: If the workflow is three sequential calls with no retry, no branching, and no durability need, a plain method is simpler. BLOGE adds value when the orchestration shape becomes complex enough that you need concurrency, resilience, or visibility.

BLOGE vs step-oriented batch frameworks

Step-oriented batch frameworks (like Spring Batch) model work as a linear pipeline of steps with chunk-oriented reading, processing, and writing.

DimensionStep-oriented batchBLOGE graph
Execution modelStep 1 → Step 2 → Step 3, linearDAG: independent nodes run in parallel automatically
IterationChunk-oriented reader/processor/writerforeach with parallel or sequential mode (batch-order-processing.bloge)
ResilienceSkip/retry policies at the step or chunk levelPer-node retry → timeout → fallback chain
Long-running waitsNot a primary concernawait with event correlation and timeout (payment-wait.bloge)
DependenciesTypically requires the framework runtimebloge-core has zero external dependencies (ADR-001)

When step-oriented batch is a better fit: If your workload is pure ETL — read chunks, transform, write — with no fan-out, no branching, and no external-event waits, a batch framework is purpose-built for that pattern. BLOGE adds value when batch steps need to fan out in parallel, wait for external signals, or share orchestration patterns with non-batch workflows.

BLOGE vs BPMN-oriented tools

BPMN-oriented tools model workflows in a visual XML-based notation, typically backed by a separate execution server.

DimensionBPMN-oriented toolsBLOGE graph
Workflow notationBPMN 2.0 XML (verbose, machine-oriented).bloge DSL (expression-capable, comment-friendly; ADR-004)
GatewaysExplicit fork/join gateway elementsImplicit from DAG shape: no gateway nodes needed
DeploymentSeparate process engine serverEmbedded library — GraphEngine runs in your JVM
Migration pathbloge-bpmn-transformer translates BPMN XML → .bloge DSL (README)

The bloge-bpmn-transformer module provides a concrete migration path. It reads BPMN 2.0 XML, lowers it through an intermediate representation, and outputs either .bloge DSL text or an executable Graph object.

Supported BPMN elements include: start/end events, service/script/user tasks, exclusive, parallel, and inclusive gateways, timer/message/signal events, call activities, and subprocess structures. Unsupported elements produce diagnostics instead of silently inventing semantics.

BPMN inclusive gateway support. The BPMN transformer supports inclusive gateways (translated to branch mode=inclusive), user task metadata preservation (assignee, candidateGroups, formKey), extension element parsing (<inputOutput> parameters and <properties>), and improved UEL-to-BLOGE expression translation (safe methods like toString(), isEmpty() no longer trigger COMPLEX_EXPRESSION diagnostics).

Translation example from BpmnToDslExample.java:

BpmnTranslator translator = new BpmnTranslator(OperatorMappingConfig.EMPTY);
try (InputStream bpmn = getClass().getResourceAsStream("/bpmn/order-process.bpmn")) {
TranslationResult<String> dsl = translator.translateToDsl(bpmn);
if (dsl.hasErrors()) {
throw new IllegalStateException(dsl.diagnostics().toString());
}
// dsl.result() is valid .bloge DSL text
Graph graph = new GraphLoader(registry).load(dsl.result());
}
Diagnostic codeMeaning
UNSUPPORTED_ELEMENTBPMN construct the translator does not support yet
UNMAPPED_OPERATORNo operator mapping matched a task
COMPLEX_EXPRESSIONUEL expression could not be translated one-to-one
POTENTIAL_DATA_LOSSTranslation possible, but some BPMN intent may not survive unchanged
AMBIGUOUS_TIMERTimer definition uses a form that does not map cleanly to BLOGE wait syntax

When BPMN tools are a better fit: If your organisation's business analysts author and own the process models in BPMN, and the execution server is already deployed and operated, migrating to BLOGE may not be worth the disruption. BLOGE adds value when the developer team owns the workflow and wants a code-friendly authoring experience with an embeddable runtime.

BLOGE vs workflow-as-code platforms

Workflow-as-code platforms let you write workflows in a general-purpose language (Go, Java, TypeScript) with durability handled by a separate server cluster that replays function histories.

DimensionWorkflow-as-code platformsBLOGE
DeploymentRequires a dedicated server clusterEmbeddable library; no external server required
Durability modelHistory replay / event sourcingCheckpoint-based: includes node outputs, loop/foreach progress, suspend context, partial results, event correlation, and session snapshots (Chapter 14, built on Chapter 13)
Workflow shapeImplicit in code control flowExplicit DAG model visible in .bloge files and Studio
DependenciesClient SDK + server infrastructurebloge-core is java.base only (ADR-001)
ConcurrencyServer manages worker task dispatchEngine uses virtual threads locally (ADR-002)
CompensationFramework-specific saga patternsDeclarative compensate per node (order-saga.bloge)

When a workflow-as-code platform is a better fit: If you need built-in multi-language support across Go, TypeScript, and Python, or you already operate the server cluster and rely on its history-replay semantics, that platform's ecosystem advantage is real. BLOGE adds value when you want to avoid operating a separate cluster, when your team is JVM-native, or when the explicit DAG model and embeddable engine matter more than cross-language SDK support.


Common Trap

❌ Feature-list comparison instead of architecture-fit comparison

A common mistake is building a spreadsheet of features — "Does it have retry? Does it have branching?" — and picking the tool with the most checkmarks.

Every tool on this page supports some form of retry, branching, and persistence. The differences that matter are architectural:

  • Where does the engine run? (embedded library vs. separate cluster)
  • How is the workflow shape expressed? (explicit model vs. implicit in code)
  • How does durability work? (checkpoints vs. replay)
  • What are the dependency and JDK requirements? (zero-dep core vs. SDK + server)

If you compare at the wrong level, you'll pick a tool that has all the features but the wrong deployment model for your team.


Guided Rewrite

Migrating from hand-written code to BLOGE in four steps

Take any orchestration method in your codebase and apply this sequence:

Step 1 — Identify the independent calls.

Mark every pair of calls that have no data dependency on each other. In the hand-written example above, fetchUser and fetchProducts are independent.

Step 2 — Extract operators.

Each call becomes an Operator<I, O> implementation. Register them in a DefaultOperatorRegistry:

registry.register("FetchUserOperator", (input, ctx) -> {
String userId = ((Map<String, Object>) input).get("userId").toString();
return Map.of("id", userId, "name", userService.fetchUser(userId).name());
});

Step 3 — Declare the graph.

Write the .bloge file. Use depends_on for explicit fan-in. Use branch on for conditional routing. Add retry, timeout, and fallback on nodes that call external services.

Step 4 — Verify behaviour.

Use GraphTestRunner from bloge-test to assert node execution order, status, and outputs:

var runner = new GraphTestRunner(
registry,
Map.of(
"FetchUserOperator", fetchUserOp,
"FetchProductsOperator", fetchProductsOp
// … remaining operators …
)
);

var result = runner.execute(graph, new GraphContext(Map.of("userId", "u1")));
runner.assertNodeExecuted("fetchUser");
runner.assertNodeExecuted("fetchProducts");
runner.assertNodeSkipped("rejectOrder"); // credit was approved

Migrating from BPMN to BLOGE

Step 1 — Translate. Use BpmnTranslator.translateToDsl() with an operator mapping config.

Step 2 — Read diagnostics. Fix any UNMAPPED_OPERATOR or COMPLEX_EXPRESSION diagnostics. Map BPMN service task IDs to BLOGE operator names in OperatorMappingConfig.

Step 3 — Review the generated DSL. The translator generates // Source: bpmn:serviceTask id="xxx" comments for traceability (when generateSourceComments is enabled).

Step 4 — Compile and run.

Graph graph = new GraphLoader(registry).load(dsl.result());
GraphEngine engine = GraphEngine.builder().registry(registry).build();
engine.execute(graph, context);

Brain Check

  1. What are the two axes in the mental model diagram, and where does BLOGE sit? (Workflow shape visibility vs. deployment topology; BLOGE is in the upper-right: declarative model + embeddable library.)

  2. Name three architectural properties of BLOGE that are documented in ADRs and explain what each trades away. (Zero-dep core → some convenience reimplemented in adapter modules. Virtual threads → requires modern JDK. HCL-inspired DSL → needs its own parser, not compatible with generic YAML/JSON tooling.)

  3. If you translate a BPMN file and get a COMPLEX_EXPRESSION diagnostic, what should you do? (Manually rewrite the UEL expression as a BLOGE path expression or move the logic into an operator.)

  4. When would you choose a workflow-as-code platform over BLOGE? (When you need multi-language SDK support across Go/TypeScript/Python, or you already operate the server cluster and rely on its replay semantics.)


Lab

Lab — Write a Falsifiable Short ADR

Choose a flow in your own system with at least five steps. Deliver an ADR of no more than one page:

  1. State three hard constraints and two preferences; each hard constraint must be decidable.
  2. Compare at least a plain method, BLOGE, and one server-based candidate.
  3. State one counterfactual under which BLOGE is not selected; “it depends” is not allowed.
  4. Design a reversible, two-week migration probe with a bounded change scope and real-effect boundary.
  5. Record a STOP / PROBE / ADOPT outcome and the evidence that would invalidate it.

Done means another reader can recover the choice, evidence, unknowns, first step, and stop condition from the ADR alone. A feature checklist is not sufficient. The reference answer and rubric are in solutions/ch33/adr-review-guide.md; do not open it first.


Bridge: from Change Attribution to Architecture Choice

This chapter consumes change-attribution evidence to make a choice; it does not turn design judgment into a controlled causal experiment. A comparison receipt can locate a changed result only when both runs share Scenario, Policy, Fixture, business runtime, and source cohort while changing exactly one of DSL_GRAPH or BLOGE_TOOLCHAIN. The ADR must still add organisational constraints, operating cost, and exit criteria. compareEvidence(...) cannot decide those for you.


Experiment acceptance card

  • Expected and observed: The constraint funnel returns STOP, PROBE, or ADOPT without assuming BLOGE wins.
  • Failure and recovery: Add a hard constraint that invalidates the choice; recover with a stop or reversible probe.
  • Proof boundary: Proves a falsifiable selection, not universal fit.
  • Exercise contract: Loan constraints; change one constraint; deliver a short ADR; stop when a counterfactual changes the decision and exit is explicit.

Recap

  • BLOGE occupies a specific architectural position: **declarative DAG model
    • embeddable library engine** with zero external dependencies in the core.
  • Compare tools on architecture-fit (deployment model, durability semantics, workflow visibility, dependency footprint), not feature checklists.
  • Hand-written code is fine for simple sequences; BLOGE adds value when you need concurrency, resilience, or visibility in the orchestration shape.
  • Step-oriented batch is purpose-built for ETL; BLOGE adds value when batch steps need fan-out, branching, or external-event waits.
  • BPMN tools excel when business analysts own the model; BLOGE adds value when developers own the workflow and want code-friendly authoring. The bloge-bpmn-transformer provides a concrete migration path.
  • Workflow-as-code platforms shine for multi-language, server-managed durability; BLOGE adds value when you want an embeddable, JVM-native engine without a separate cluster.
  • Migration from hand-written code follows four steps: identify independence, extract operators, declare the graph, verify behaviour.
  • A decision produces STOP / PROBE / ADOPT: stop on a violated hard constraint and probe when a decisive fact is unknown.
  • A short ADR must contain the choice, evidence, counterfactual, first migration step, and exit criteria.

Next Step

In Chapter 34 — Labs you will work through end-to-end exercises that combine everything from the series — graphs, resilience, subgraphs, durability, observability, and the migration patterns from this chapter — into complete, runnable projects.


Coding Agent: Open the versioned task guide.