Skip to content

Getting Started with BLOGE

This guide walks from a single node to a small production-style setup. Every section shows both the Java builder style and the equivalent .bloge DSL so teams can choose the authoring style that fits their workflow.

Prerequisites

  • Java 25+
  • Maven 3.6+
  • Node.js 18+ only if you want to run Bloge Studio locally

Add the BLOGE dependencies you need to your pom.xml. At minimum, bloge-core is always required. See Appendix: Maven dependencies for the full coordinates of all modules.

Available modules

ModuleDescriptionWhen to add
bloge-coreCore engine with zero external dependencies (java.base only)Always
bloge-core-extSession/phase/round extension runtime and compilersSession, phase, or round flows
bloge-dslExternal DSL parser and compiler for .bloge filesWhen loading .bloge files
bloge-durableDurable execution support: InMemory and JDBC checkpoint storesLong-running or resumable flows
bloge-durable-codecCheckpoint codec implementations for durable moduleTogether with bloge-durable
bloge-springSpring Boot auto-configuration for BLOGESpring Boot apps
bloge-metrics-otelMicrometer metrics, Micrometer Tracing spans, OpenTelemetry context propagation, SLF4J MDC propagationObservability
bloge-common-operatorsProduction-grade common operator library — HTTP, DB, Messaging, AI/LLM, Transform, Notification, Approval, Storage, Crypto, ValidationPre-built operator library
bloge-lintLint tool and CLI for .bloge DSL filesDSL authoring or CI lint
bloge-testTest tools: MockOperator, GraphTestRunner, DslTestHelperTesting
bloge-bpmn-transformerBPMN 2.0 XML to BLOGE DSL and Graph translator for legacy system migrationBPMN migration
bloge-scriptOptional sandboxed Groovy script execution for bloge script nodesScript nodes

1. Single-node echo graph

The smallest useful graph is one node that reads context and returns a value.

Java API

java
Graph graph = Graph.builder("echo")
    .node("echo", (Map<String, Object> input, OperatorContext ctx) ->
        Map.of("message", ctx.graphContext().get("message", String.class)))
    .build();

GraphResult result = GraphEngine.builder().build()
    .execute(graph, new GraphContext(Map.of("message", "hello")));

DSL

bloge
graph echo {
  node echo : EchoOperator {
    input {
      message = ctx.message
    }
  }
}
java
DefaultOperatorRegistry registry = new DefaultOperatorRegistry();
registry.register("EchoOperator", (input, ctx) -> input);

Graph graph = new GraphLoader(registry).load(dslSource);
GraphResult result = GraphEngine.builder().registry(registry).build()
    .execute(graph, new GraphContext(Map.of("message", "hello")));

What to notice:

  • Java is ideal when operators and schemas already live in code.
  • DSL is ideal when you want a reviewable artifact that can be edited without recompiling Java classes.

2. Two-node sequential graph

Now add a downstream node that depends on the first node's output.

Java API

java
Graph graph = Graph.builder("greetUser")
    .node("fetchUser", fetchUserOperator)
    .node("formatGreeting", formatGreetingOperator)
        .dependsOn("fetchUser")
    .build();

DSL

bloge
graph greetUser {
  node fetchUser : FetchUserOperator {
    input {
      userId = ctx.userId
    }
  }

  node formatGreeting : FormatGreetingOperator {
    input {
      user = fetchUser.output
    }
  }
}

The DSL compiler infers the dependency from fetchUser.output, so you do not have to repeat depends_on unless you want to make the edge explicit.


3. Parallel fan-out plus branch

BLOGE becomes interesting when multiple upstream calls can run concurrently and a later step decides which path to take.

Java API

java
Graph graph = Graph.builder("orderDecision")
    .node("fetchUser", fetchUserOperator)
    .node("fetchProducts", fetchProductsOperator)
    .node("calcPrice", calcPriceOperator)
        .dependsOn("fetchUser", "fetchProducts")
    .branch("calcPrice")
        .on("approved")
        .when(value -> Boolean.TRUE.equals(value), "createOrder")
        .otherwise("rejectOrder")
    .node("createOrder", createOrderOperator)
    .node("rejectOrder", rejectOrderOperator)
    .build();

DSL

bloge
graph orderDecision {
  node fetchUser : FetchUserOperator {
    input {
      userId = ctx.userId
    }
  }

  node fetchProducts : FetchProductsOperator {
    input {
      productIds = ctx.productIds
    }
  }

  node calcPrice : CalcPriceOperator {
    input {
      user = fetchUser.output
      products = fetchProducts.output
    }
  }

  branch on calcPrice.output.approved {
    true -> createOrder
    otherwise -> rejectOrder
  }

  node createOrder : CreateOrderOperator {}
  node rejectOrder : RejectOrderOperator {}
}

What to notice:

  • fetchUser and fetchProducts can run in parallel because nothing depends on the other.
  • the branch is control flow, not data transformation; use transform when you only want to compute a value.

4. Java and DSL are equivalent authoring styles

The same business graph can be expressed in either form. A practical workflow is:

  1. prototype the graph in DSL
  2. prove behavior with tests or golden baselines
  3. move stable operator logic into Java classes
  4. keep the graph shape in DSL or generate it from Studio

Java API

java
Graph graph = Graph.builder("ticketRouting")
    .node("fetchCustomer", fetchCustomerOperator)
    .node("analyzeSentiment", sentimentOperator)
        .dependsOn("fetchCustomer")
    .branch("analyzeSentiment")
        .on("priority")
        .when(value -> "vip".equals(value), "assignVipAgent")
        .when(value -> "normal".equals(value), "assignNormalAgent")
        .otherwise("autoResolve")
    .node("assignVipAgent", assignVipAgentOperator)
    .node("assignNormalAgent", assignNormalAgentOperator)
    .node("autoResolve", autoResolveOperator)
    .build();

DSL

bloge
graph ticketRouting {
  node fetchCustomer : FetchCustomerOperator {
    input {
      ticketId = ctx.ticketId
    }
  }

  node analyzeSentiment : AnalyzeSentimentOperator {
    input {
      customer = fetchCustomer.output
    }
  }

  branch on analyzeSentiment.output.priority {
    "vip" -> assignVipAgent
    "normal" -> assignNormalAgent
    otherwise -> autoResolve
  }

  node assignVipAgent : AssignVipAgentOperator {}
  node assignNormalAgent : AssignNormalAgentOperator {}
  node autoResolve : AutoResolveOperator {}
}

Use the Java style when type-safe composition inside the same codebase is more important than external authoring. Use DSL when product, operations, or tooling teams need a standalone graph artifact.


5. Spring Boot integration

bloge-spring lets a normal Spring Boot app publish operators and load .bloge files automatically.

Java API inside Spring Boot

java
@Configuration
class BlogeGraphs {
    @Bean
    Graph orderGraph() {
        return Graph.builder("orderGraph")
            .node("fetchUser", new FetchUserOperator())
            .build();
    }
}

DSL inside Spring Boot

Add the dependency:

xml
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-spring</artifactId>
    <version>0.3.1</version>
</dependency>

Create an operator bean:

java
@BlogeOperator("FetchUserOperator")
@Component
class FetchUserOperator implements Operator<Map<String, Object>, Map<String, Object>> {
    @Override
    public Map<String, Object> execute(Map<String, Object> input, OperatorContext ctx) {
        return Map.of("id", input.get("userId"));
    }
}

Create src/main/resources/bloge/order-graph.bloge:

bloge
graph orderGraph {
  node fetchUser : FetchUserOperator {
    input {
      userId = ctx.userId
    }
  }
}

At runtime, inject GraphEngine and the discovered graphs, then execute the graph that matches your route or use case.


6. Studio visualization

bloge-studio is the visual editor for teams who want to model a graph before checking in the DSL.

Studio workflow

  1. start the UI locally:
bash
cd bloge-studio
npm install
npm run dev
  1. create nodes and edges visually
  2. export the generated .bloge source
  3. commit the DSL file into src/main/resources/bloge/
  4. execute it with the same GraphLoader flow shown earlier

DSL output from Studio

bloge
graph studioDraft {
  node fetchProfile : FetchProfileOperator {
    input {
      userId = ctx.userId
    }
  }

  node enrichProfile : EnrichProfileOperator {
    input {
      profile = fetchProfile.output
    }
  }
}

Equivalent Java API

java
Graph graph = Graph.builder("studioDraft")
    .node("fetchProfile", fetchProfileOperator)
    .node("enrichProfile", enrichProfileOperator)
        .dependsOn("fetchProfile")
    .build();

Studio is best treated as an authoring surface, not a separate runtime. The execution path remains the same Java engine that runs every other BLOGE graph.


Next steps

  • Prefer a chapter-by-chapter path? Follow Head First BLOGE for the full tutorial track in English and Chinese.
  • Browse Example Catalog for larger runnable graphs.
  • Read Full Specification for the full language contract.
  • If you need long-running conversational flows, continue with the session / phase / round extension docs in the repository and the DSL reference in this site.
  • If you need production persistence or observability, continue with Durable Flows and Observability.

Appendix: Maven dependencies

All BLOGE modules share the same groupId and version. Copy the coordinates for the modules your project requires.

xml
<!-- Core engine — always required -->
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-core</artifactId>
    <version>0.3.1</version>
</dependency>

<!-- Session/phase/round extension runtime and compilers -->
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-core-ext</artifactId>
    <version>0.3.1</version>
</dependency>

<!-- External DSL parser and compiler for .bloge files -->
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-dsl</artifactId>
    <version>0.3.1</version>
</dependency>

<!-- Durable execution support: InMemory and JDBC checkpoint stores -->
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-durable</artifactId>
    <version>0.3.1</version>
</dependency>

<!-- Checkpoint codec implementations for durable module -->
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-durable-codec</artifactId>
    <version>0.3.1</version>
</dependency>

<!-- Spring Boot auto-configuration for BLOGE -->
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-spring</artifactId>
    <version>0.3.1</version>
</dependency>

<!-- Observability: Micrometer metrics, Micrometer Tracing spans, OpenTelemetry, SLF4J MDC -->
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-metrics-otel</artifactId>
    <version>0.3.1</version>
</dependency>

<!-- Production-grade common operator library: HTTP, DB, Messaging, AI/LLM, Transform, etc. -->
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-common-operators</artifactId>
    <version>0.3.1</version>
</dependency>

<!-- Lint tool and CLI for .bloge DSL files -->
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-lint</artifactId>
    <version>0.3.1</version>
</dependency>

<!-- Test tools: MockOperator, GraphTestRunner, DslTestHelper -->
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-test</artifactId>
    <version>0.3.1</version>
</dependency>

<!-- BPMN 2.0 XML to BLOGE DSL and Graph translator -->
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-bpmn-transformer</artifactId>
    <version>0.3.1</version>
</dependency>

<!-- Optional sandboxed Groovy script execution for bloge script nodes -->
<dependency>
    <groupId>com.leanowtech.bloge</groupId>
    <artifactId>bloge-script</artifactId>
    <version>0.3.1</version>
</dependency>