Compile your Java event handlers to a flat dispatcher you can read.

Fluxtion is the compiler for event-driven logic. Annotate POJOs with @OnEventHandler; the compiler infers the event graph and emits a deterministic dispatcher class — readable, audit-instrumented and replayable from the same input events. Execution inference like type inference, but for your event-handling dispatch.

You write
// Counts trades.
public class TradeCounter {
    private int count;
 
    @OnEventHandler
    public boolean onTrade(Trade t) {
        count++;
        return true;
    }
    public int count() { return count; }
}
 
// Reacts to the counter — the constructor ref is the wiring.
public class TradePublisher {
    private final TradeCounter counter;
    public TradePublisher(TradeCounter counter) {
        this.counter = counter;
    }
 
    @OnTrigger
    public boolean publishCount() {
        System.out.println("trades: " + counter.count());
        return true;
    }
}
 
// Add only the child — Fluxtion discovers TradeCounter.
var flow = Fluxtion.compile(new TradePublisher(new TradeCounter()));
flow.onEvent(new Trade("AAPL", 100));
Fluxtion generates
// Generated by Fluxtion — plain Java, lives in your repo.
public final class TradeProcessor implements EventProcessor {
 
    private final TradeCounter   tradeCounter = new TradeCounter();
    private final TradePublisher publisher =
            new TradePublisher(tradeCounter);
 
    @Override
    public void onEvent(Object event) {
        if (event instanceof Trade) {
            handleEvent((Trade) event);
        }
    }
 
    private void handleEvent(Trade typedEvent) {
        boolean tradeCounter_updated =
                tradeCounter.onTrade(typedEvent);
 
        if (tradeCounter_updated) {
            publisher.publishCount();
        }
    }
}

Open source · Java 8+ · zero install · runtime is one jar

What

Fluxtion is an ahead-of-time compiler for event-driven Java. Source-gen, not reflection. The artifact is a plain Java class.

How

Annotate POJOs with @OnEventHandler / @OnTrigger. The compiler walks the graph at build time and emits a deterministic dispatcher class.

Why it's different

The generated source is the audit. Replay reproduces the same decisions from the same input events. Your runtime classpath is fluxtion-runtime + your code — no broker, no actor system, no DAG to deploy.

The paradigm

This is inferred orchestration programming

The application logic is ordinary Java. The orchestration is not hand-written, configured in a workflow tool, or discovered by a runtime scheduler.

Fluxtion infers orchestration from explicit structure — annotated handlers, triggers, dependencies and the object graph — and generates a dispatcher as plain Java, so the runtime coordination is visible, testable and replayable.

What inferred orchestration programming is →
You provideFluxtion infersGenerated Java shows
@OnEventHandlerevent entry pointonEvent(...)
object-graph dependencydispatch orderhandleEvent(...)
@OnTriggertrigger propagationnode.onTrigger()
state dependencychange propagationif (isDirty_...)
processor definitionexecutable orchestrationProcessor.java

Source generation, not runtime reflection. Generated Java, not a hidden runtime DAG. Ordinary POJOs, not a bespoke workflow language. The generated artefact is the product.

Where it sits

If you've used these, here's how Fluxtion is different

Like Akka Streams

…but compile-time. Single dispatcher class instead of an actor system. No message-protocol design.

Like Kafka Streams

…but local and embeddable. No broker required. Your dispatcher is just a Java class, instantiate it where you like.

Like Flink

…but the topology is a .java file you read. No .uid() load-bearing for savepoint compatibility.

Like RxJava

…but the generated code lives in your repo. The reactive graph compiles to ordinary methods you can step through in a debugger.

Where Fluxtion sits vs Kafka, Aeron, Chronicle, RxJava, Hazelcast and Spring Modulith: compare → · Longer story: alternative comparisons

The trick

The artifact is the product

Fluxtion walks your annotated POJOs at compile time and emits a flat Java class containing dispatch order, trigger graph, and audit hook points. Your runtime dependency is fluxtion-runtime + your code — no compiler at startup, no reflection in the hot path, no DAG to deploy. The output is plain Java: your IDE navigates it, your VCS diffs it, your auditor reads it.

Run it — one file, jbang
//DEPS com.telamin.fluxtion:fluxtion-builder:1.0.8
//JAVA 21
package demo;
import com.telamin.fluxtion.Fluxtion;
import com.telamin.fluxtion.runtime.DataFlow;
import com.telamin.fluxtion.runtime.annotations.OnEventHandler;
import com.telamin.fluxtion.runtime.annotations.OnTrigger;
 
public class Trades {
    public static void main(String[] a) {
        // Add only the child — Fluxtion discovers TradeCounter and
        // emits the dispatcher below.
        DataFlow flow = (DataFlow) Fluxtion.compileAot(
                "demo.generated", "TradeProcessor",
                new TradePublisher(new TradeCounter()));
        flow.init();
        flow.onEvent(new Trade("AAPL", 100));
        System.out.println("""
 
                See generated Fluxtion dispatcher here:
                less src/main/java/demo/generated/TradeProcessor.java""");
    }
 
    public record Trade(String symbol, int qty) {}
 
    public static class TradeCounter {
        private int count;
        @OnEventHandler public boolean onTrade(Trade t) {
            count++;
            System.out.println("received -> " + t);
            return true;
        }
        public int count() { return count; }
    }
 
    public static class TradePublisher {
        private final TradeCounter counter;
        public TradePublisher(TradeCounter counter) { this.counter = counter; }
        @OnTrigger public boolean publishCount() {
            System.out.println("trades: " + counter.count());
            return true;
        }
    }
}
Generated dispatcher
// Generated by Fluxtion. This is the file you read,
// audit, diff in your VCS, and ship to production.
 
public final class TradeProcessor implements EventProcessor {
 
    private final TradeCounter   tradeCounter = new TradeCounter();
    private final TradePublisher publisher =
            new TradePublisher(tradeCounter);
 
    @Override
    public void onEvent(Object event) {
        if (event instanceof Trade) {
            handleEvent((Trade) event);
        }
    }
 
    // Strongly-typed dispatch. One method per event type.
    // The dispatcher's structure IS your event graph — every
    // node call, every trigger edge, made explicit.
    private void handleEvent(Trade typedEvent) {
        boolean tradeCounter_updated =
                tradeCounter.onTrade(typedEvent);
 
        // @OnTrigger fan-out, in dependency order.
        if (tradeCounter_updated) {
            publisher.publishCount();
        }
    }
}

jbang runs a single .java file directly — no Maven, no project. It reads the //DEPS lines at the top, fetches those jars, compiles the file, and runs main: jbang Trades.java.

That compile step runs on Fluxtion's cloud generator — it turns your topology into the dispatcher and hands it back. It's a service with a free tier for development: drop a key in ~/.fluxtion and every jbang run just works. (The playground is keyless because it bundles a local generator.) Get a free-tier key →

Run it — with a key
$ jbang Trades.java
[jbang] Building jar for Trades.java...
INFO: Using compiler strategy: javac
received -> Trade[symbol=AAPL, qty=100]
trades: 1

See generated Fluxtion dispatcher here:
less src/main/java/demo/generated/TradeProcessor.java
Missing key — what failure looks like
$ jbang Trades.java
  apiKey=MISSING_KEY

  Register and create an API key here:
    https://rapidapi.com/hub

Exception in thread "main" java.io.IOException: Remote source
generation failed with HTTP 401: {"message":"Invalid API key.
Go to https://docs.rapidapi.com/docs/keys for more info."}

That 401 is expected, not a bug — no key, no compile. The generator is a service, and its free tier covers local dev.

Want zero setup? The browser playground is keyless — the same example, generated in-page, nothing to install. Run it keyless in the playground →

The core graph-inference mechanism is patented (US filing 2026). The runtime is open source (AGPL today; Apache 2.0 under consideration) and carries a patent grant for its use.

Generation strategy

The graph structure is a choice, not a constraint

Same nodes, same generated dispatcher — what you choose is when Fluxtion builds the graph. Bake it at build time for a fixed, audit-ready binary, or generate it in-process so the topology can evolve while the application runs.

AOT — built once

Bake a fixed structure

Fluxtion emits the dispatcher as a plain .java file during mvn compile. The structure is fixed and committed to your repo; your only runtime dependency is fluxtion-runtime. Auditable, native-image-ready, zero startup cost — the right shape when the topology is known and stable.

In-process — built when you run

Let the structure evolve

Fluxtion builds the graph at startup, so the structure isn't fixed: when your topology needs to change — add nodes, rewire branches, swap a rule — regenerate it in-process and keep running. The trade: the generator has to be reachable at runtime (a key in ~/.fluxtion, or the local generator on the classpath).

Either way you get the same dispatcher and the same audit trail — only the timing differs. See every authoring channel × generation mode →

What that buys you

Two things the artifact buys you

One mechanism — source-gen of a flat readable dispatcher — produces both. Read each from the generated .java.

Your code, your team

Zero integration cost

Dispatch order, trigger fan-out, lifecycle propagation, audit hook points — the compiler emits them. They're no longer code you maintain; they're code you read. Integration becomes a compile-time problem, solved once. The same property that makes vendor JARs free to consume applies to every component you write yourself.

The pattern, externalised
By construction, not by tuning

Mechanical sympathy

Flat structure is a branch-predictor's happy place. No reflection in the runtime path; the JIT inlines aggressively. Nodes allocate once at startup; the dispatcher stays hot in L1 icache. Zero per-event allocation from the substrate — your handlers choose the rest. The structure is the optimisation; you don't tune it, you just don't fight it.

Performance — server speed on a phone
The toolbox

The ecosystem around the compiler

The compiler is the core; these are the tools around it — grouped by where you are in the lifecycle: build it, ship it, prove it.

Build with AI

Built for LLM authoring.

Fluxtion is unusually suited to AI authoring: you write intent (nodes + wiring), and the compiler guarantees determinism, audit and replay — a careless author can't introduce a race or a non-deterministic decision; the source-gen step enforces it.

You write nodes; the compiler wires them. Each node is ~30 lines, so a model reasons locally — one node at a time — and the compiler does the topology. Its errors are directive (use @AssignToField), so a model with a run loop self-corrects instead of guessing.

There's no closed operator library to learn: any Java method is a candidate operator, so a model writes against the JDK and your domain code, not a framework API it has to memorise.

Point your assistant at the Fluxtion author guide and golden-path doc and it bootstraps itself — the conventions, the gotchas, the troubleshooting index, in one place. Then it iterates against real compiler feedback until the graph builds.

Bootstrap your LLM for Fluxtion
The project

Open source, patent-backed

Open source, one runtime jar, developed in the open since 2019. The badges devs scan for — stars, latest release, licence, good-first-issues — and a place to start.

GitHub stars Latest release Runtime licence Good first issues

Built by Greg Higgins. Fluxtion has been in development since 2019; 1.0 shipped in May 2026. The core graph-inference mechanism is patented — the open-source runtime carries a patent grant for its use.