Online edition for BLOGE
0.9.8-RC1· facts verified 2026-09-15 · 中文
Chapter 1 — What BLOGE Is and Why It Exists
This chapter establishes a working understanding of what BLOGE is, why it exists, and which structural properties define it most clearly.
Learning Goals
- Describe BLOGE in one sentence without falling back to framework jargon.
- Name the three properties that make BLOGE different from ad-hoc orchestration: visible, structured, and resilient.
- Describe what a graph is at the conceptual level — a directed acyclic graph of business steps with explicit data and control flow.
- Explain why the engine, not the developer, should be responsible for concurrency, retry, and failure propagation.
Prerequisites
- Basic Java knowledge (you don't need to be an expert).
- A working JDK 25+ and Maven 3.6+ installation (see Getting Started).
Source Examples
| File | What it shows |
|---|---|
ch05/order-process.bloge | A complete teaching workflow with fan-out, fan-in, branching, and resilience |
ch03/bff-dashboard.bloge | A BFF aggregation graph with five parallel fetches and mixed fallback strategies |
30-Second Introduction
At a high level, BLOGE is an orchestration engine for business workflows. It does not replace business code itself; instead, it extracts runtime concerns such as dependency relationships, parallel execution, and failure handling from scattered implementations and organizes them into an explicit graph.
Defining BLOGE
BLOGE is a Java-first orchestration engine. It represents business workflows as graphs of nodes and dependencies, while the engine handles ordering, parallelism, retry, timeout, branching, waiting, and recovery. Runtime logic that would otherwise be scattered across service code is therefore collected into an explicit structure.
BLOGE extends beyond DAG-based graphs into higher-level orchestration
patterns: multi-turn sessions (bloge-session-ext + bloge-session-durable),
explicit state machines (bloge-state-ext + bloge-state-durable), and
LLM-driven agent loops with tool dispatch (bloge-agent-ext). These
capabilities are covered in later chapters — for now, the graph model is
the foundation.
BLOGE stands for Biz Logic Orchestration Graph Engine. The expanded name
states the system boundary directly: it is concerned with business logic, it
solves an orchestration problem, it uses a graph as the execution model,
and it relies on an engine to run that model.
In that sense, BLOGE is neither only a DSL nor only a diagramming tool. It combines a workflow model with a runtime engine; once workflow shape, dependencies, and failure policies are declared, the engine applies them to execution, concurrency control, and resilience behavior.
Three Design Properties Used in This Book
This book uses three properties to compare BLOGE with ad-hoc orchestration. They describe how the workflow is read, how execution is organized, and how runtime behavior is attached to the model; they are an explanatory frame, not an independent benchmark result.
- Visible — the workflow is presented directly as a graph, so business paths, branch points, and upstream-downstream relationships can be read from the model instead of reconstructed from service code.
- Structured — dependencies belong to the workflow model itself. Which nodes may run in parallel and which nodes must wait is determined by declared graph relationships rather than by the order of handwritten code.
- Resilient — retry, timeout, fallback, waiting, and durable progress are attached to the workflow as runtime policies. Failure handling therefore becomes part of the model instead of being scattered across ad-hoc glue code.
What BLOGE Is Not
Defining what BLOGE is not helps establish the boundary of the system just as clearly. The following interpretations are incomplete because they describe surface form without describing BLOGE as a workflow model plus runtime engine.
- It is not another wrapper around sequential code. If the only change were syntactic, execution shape would still be determined mainly by writing order; in BLOGE, execution shape is determined by dependencies declared in the graph.
- It is not only a diagramming tool. A graph here is not a static picture for documentation; it is a workflow model that the engine can read, schedule, and execute.
- It is not a system that leaves orchestration mechanics to each application team. Concurrency, retry, timeout, fallback, and waiting do not have to be rebuilt around every business step; the engine applies them consistently from the model.
The simplest accurate mental shorthand is this: BLOGE turns workflow intent into an explicit graph, then lets the engine carry the operational burden.
Why Not Hand-Written Orchestration
Knowing what BLOGE is is only the first step. The harder question is why teams should stop writing orchestration as sequential service code with ad-hoc retry, timeout, and recovery logic spread across multiple classes. That is where hand-written orchestration starts to fail.
Why This Matters
Most business systems start simple:
1. Fetch the user
2. Fetch the products
3. Calculate the price
4. Check credit
5. Create the order (or reject it)
In a plain service, you write this as sequential method calls. It works — until it doesn't:
- Steps 1 and 2 are independent but you run them in sequence, wasting wall-clock time.
- Step 4 can fail transiently, so you wrap it in a retry loop — and now your business logic is tangled with infrastructure.
- Nobody can see the workflow. When something breaks at 2 AM, a developer has to read the code to reconstruct what happened.
This is the problem BLOGE exists to solve. Once you know that BLOGE is an engine for explicit workflow graphs, the value proposition becomes much clearer: you declare the shape of your workflow — which steps depend on which, what to do when something fails — and the engine handles scheduling, concurrency, and resilience for you.
Mental Model
Think of a BLOGE graph as a recipe card for the engine:
Each box is a node. Each arrow is a dependency. The engine reads this card and figures out:
fetchUserandfetchProductshave no dependencies on each other → run them in parallel.calcPriceneeds both → wait for both, then start.checkCreditfeeds a branch — the engine picks the right downstream node based on the result.
You describe what depends on what. The engine decides when to run what.
First Working Example
Here is a simplified extract from
ch05/order-process.bloge:
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
}
}
Read it top to bottom:
- Two independent fetches — the engine will run them concurrently.
fetchUsereven has its own retry policy. - A calculation node with an explicit
depends_onthat fans in from both fetches — the engine could also infer this from the.outputreferences. - A credit check (
CreditCheckOperator) with retry and fallback — resilience declared right where the node is defined. - A branch — not an
ifstatement in your Java code, but a first-class routing decision in the graph.
Key insight: You never wrote a thread pool, a retry loop, or a try/catch for the fallback. The engine owns that.
Follow One Order: Move Responsibilities, Not Methods
Imagine OrderService.placeOrder() started as five readable calls. Six months
later it also opens futures, retries credit checks, times out inventory, catches
half a dozen exceptions, emits metrics, and decides whether to create or reject
the order. The method still "works", but one place now owns both the business
story and every mechanism required to run it.
Read the diagram as an ownership change
BLOGE does not improve the design merely by drawing boxes around those calls. The useful change is that each concern gets one visible owner:
| Question | Visible owner |
|---|---|
| What work exists? | Nodes such as fetchUser, calcPrice, and createOrder |
| What must finish first? | Dependency edges |
| What happens after a transient failure? | The node's retry and timeout policy |
| Which business path was not selected? | The result status SKIPPED |
| What did the business operation actually do? | The Operator output and external evidence |
This separation matters during change. Adding an inventory check changes the
graph shape. Changing the pricing formula changes CalcPriceOperator. Changing
the retry delay changes a node policy. A reviewer no longer has to rediscover
all three decisions inside one control-flow method.
The first boundary to remember
An explicit graph makes the declared orchestration inspectable; it does not prove that an Operator charged the right amount or that the workflow satisfies the business requirement. Chapters 18 and 23–32 add those stronger forms of evidence. For now, the win is narrower and concrete: the execution shape has stopped hiding inside incidental Java control flow.
The Module Map — Now You Have a Reason to Read It
The order story gives each layer a job. Core runs the graph; authoring tools make the declaration readable; extensions add larger interaction models; runtime integrations retain and observe execution.
Layer 1 — Core
| Module | What it provides |
|---|---|
bloge-core | Graph model, scheduler, listener/interceptor APIs, and GraphEngine. |
bloge-runtime-spi | Execution, checkpoint, wait, timer, lease, audit, and inbox store contracts. |
Layer 2 — Authoring
| Module | What it provides |
|---|---|
bloge-dsl | .bloge parser, compiler, and code generator. |
bloge-lang / bloge-lsp | Shared language model and editor diagnostics. |
bloge-lint / bloge-studio | Reproducible rules and visual graph authoring. |
bloge-maven-plugin | Operator metadata generation. |
Layer 3 — Extensions
| Module | What it provides |
|---|---|
bloge-session-ext / bloge-state-ext | Multi-turn sessions and explicit state machines. |
bloge-agent-ext | Model/tool loops with bounded capabilities. |
bloge-event-journal | Append-only execution and agent events. |
Layer 4 — Runtime and integrations
| Module | What it provides |
|---|---|
bloge-durable / bloge-durable-mybatis | Recovery and persistent execution state. |
bloge-spring / bloge-spring-web | Boot wiring, web errors, and the operations console. |
bloge-metrics-otel / bloge-dispatch-kafka | Telemetry and remote-worker dispatch. |
bloge-test / bloge-verification | Graph tests and business-correctness evidence. |
Start with bloge-core and bloge-dsl. Add a layer only when the story reaches
its problem; the rest of the book returns to this map at those moments.
Break It Apart
| Concept | What it means |
|---|---|
graph | A named container for the workflow — a DAG of nodes |
node | A single processing step that runs an operator |
input { … } | Bindings that wire upstream outputs and context values into the node's input |
ctx.userId | A value from the graph context — the initial data passed to the engine at execution time |
fetchUser.output | The output of an upstream node — referencing it creates an implicit dependency |
timeout | A wall-clock duration limit for the node |
retry | Automatic re-execution on failure with a backoff strategy |
fallback | A static result to use if the node still fails after retries |
branch on | Conditional routing that activates one downstream path based on a node's output |
Common Trap
❌ Treating nodes like function calls
// DON'T think like this:
User user = fetchUser(ctx.getUserId());
List<Product> products = fetchProducts(ctx.getProductIds());
Price price = calcPrice(user, products);
In procedural code the order of lines is the execution order. In BLOGE the dependency graph is the execution order. If two nodes don't reference each other's output, the engine is free to run them at the same time.
Think in dependencies, not in sequences.
Guided Rewrite
Look at the
ch03/bff-dashboard.bloge
example. It fetches five data sources for a dashboard:
graph bffDashboard {
node fetchProfile : FetchProfileOperator {
input { userId = ctx.userId }
timeout = 2s
retry = { attempts: 1, backoff: 100ms }
}
node fetchOrders : FetchOrdersOperator {
input { userId = ctx.userId }
timeout = 3s
fallback = { recentOrderIds: [], totalOrders: 0 }
}
// … fetchRecommendations, fetchNotifications, fetchLoyalty …
node aggregate : AggregateOperator {
depends_on = [fetchProfile, fetchOrders, fetchRecommendations, fetchNotifications, fetchLoyalty]
input {
profile = fetchProfile.output
orders = fetchOrders.output
recommendations = fetchRecommendations.output
notifications = fetchNotifications.output
loyalty = fetchLoyalty.output
}
}
}
Questions to consider:
- How many nodes run in parallel? (Answer: all five fetch nodes — none depends on another.)
- What happens if
fetchOrdersfails or times out? (Answer:fetchOrdershas no retry policy — it only has a fallback. If it fails the fallback value{ recentOrderIds: [], totalOrders: 0 }is used and the graph continues with partial data.) - If you added a sixth fetch that depended on
fetchProfile.output, would it still run in parallel with the other fetches? (Answer: no — it would wait forfetchProfileto complete first.)
Brain Check
- What are the three properties that distinguish a BLOGE graph from ad-hoc orchestration code? (Visible, structured, resilient.)
- Who decides whether two nodes run concurrently — the developer or the engine? (The engine, based on the dependency graph.)
- In the order-process example, what happens if
checkCreditfails after all three retry attempts? (The fallback value is used:{ approved: false, reason: "credit service unavailable" }.)
Lab
Open ch05/order-process.bloge
in your editor.
- Add a
fetchInventorynode that runs in parallel withfetchUserandfetchProducts. Give it atimeoutof2s. - Wire its output into
calcPriceby adding an input binding. - Predict the new execution shape: which nodes now run in parallel? Which wait?
Compilation can wait here. The immediate goal is to make the shape of the DSL readable before moving on to execution in Chapter 2.
Experiment acceptance card
- Expected and observed: Dependencies and resilience policies are visible in DSL, and the execution shape can be labelled.
- Failure and recovery: Reference a missing output; remove the bad edge and redraw the DAG.
- Proof boundary: Proves reviewable design intent, not compilation, execution, or business correctness.
- Exercise contract: Order DSL; add only the inventory node and one binding; deliver a dependency sketch; stop when every edge has a source and the graph is acyclic.
Recap
- BLOGE replaces hand-written orchestration with a visible, structured, resilient graph.
- A graph is a DAG of nodes. Each node runs an operator.
- The engine reads dependencies (explicit or inferred from
.outputreferences) and schedules nodes accordingly. - Resilience (timeout, retry, fallback) is declared on the node, not buried in business code.
- Branches route control flow based on a node's output.
Next Step
Chapter 2 — Your First Graph continues with the smallest graph that can be written, compiled, and executed end to end.
Reference Links
- Project README — module overview and quick start
- Getting Started — full setup guide
- Core Architecture — engine internals
ch05/order-process.bloge— full example sourcech03/bff-dashboard.bloge— full example source
Coding Agent: Open the versioned task guide.