Lightweight Java library for ordered decision tree evaluation, reusable value resolution, and traceable execution.
Business code often starts with a few simple if-else checks and gradually turns into long, nested rule chains that are harder to read, test, and debug.
decision-tree-engine is a small Java library for cases where decision logic is better represented as an ordered tree:
- evaluate conditions in a predictable order
- return the first matching result
- support nested branches
- optionally capture an execution trace to explain why a value was selected
It is especially useful when rule-based selection logic grows beyond a few straightforward conditions but does not justify a heavy rule engine.
This project is intentionally not positioned as a replacement for mature rule platforms such as Drools, Easy Rules, or other general-purpose rule engines. Its scope is smaller and more explicit:
- code-first rule definition
- deterministic tree-shaped evaluation
- lightweight embedding into ordinary Java applications
- no external DSL
- no runtime rule authoring
- no framework-heavy setup
- ordered first-match evaluation
- nested decision branches
- fallback result on parent nodes
- immutable runtime model
- fluent builder API
- execution trace / explain mode
- reusable date-based conditions for
LocalDate
- Java 17+
At the moment, the project is intended to be used directly from source or as a local library dependency.
import io.github.reloadall.decisiontree.api.DecisionResult;
import io.github.reloadall.decisiontree.builder.DecisionTree;
import io.github.reloadall.decisiontree.engine.DecisionEngine;
public class Example {
public static void main(String[] args) {
DecisionEngine<Integer, String> engine = DecisionTree.<Integer, String>root("root")
.when("greater than 10", value -> value > 10)
.returnValue(value -> "gt10")
.end()
.when("greater than 5", value -> value > 5)
.returnValue(value -> "gt5")
.build();
DecisionResult<String> result = engine.evaluate(7);
System.out.println(result.matched()); // true
System.out.println(result.value()); // gt5
}
}This example is close to the original use case that motivated the library: selecting a resulting date based on multiple date comparisons.
import io.github.reloadall.decisiontree.api.EvaluationReport;
import io.github.reloadall.decisiontree.builder.DecisionTree;
import io.github.reloadall.decisiontree.date.DateConditions;
import io.github.reloadall.decisiontree.engine.DecisionEngine;
import java.time.LocalDate;
public class DateExample {
public static void main(String[] args) {
DecisionEngine<SampleDto, LocalDate> engine = DecisionTree.<SampleDto, LocalDate>root("root")
.when("date1 <= date2", DateConditions.lessOrEqual(SampleDto::date1, SampleDto::date2))
.when("date3 <= date4", DateConditions.lessOrEqual(SampleDto::date3, SampleDto::date4))
.returnValue(SampleDto::date1)
.end()
.when("date3 > date4", DateConditions.greater(SampleDto::date3, SampleDto::date4))
.returnValue(SampleDto::date2)
.end()
.end()
.when("date2 < date1", DateConditions.less(SampleDto::date2, SampleDto::date1))
.returnValue(SampleDto::date2)
.build();
SampleDto dto = new SampleDto(
LocalDate.of(2026, 3, 1),
LocalDate.of(2026, 3, 10),
LocalDate.of(2026, 3, 5),
LocalDate.of(2026, 3, 8)
);
EvaluationReport<LocalDate> report = engine.evaluateWithTrace(dto);
System.out.println(report.result().value());
System.out.println(report.trace());
}
public record SampleDto(
LocalDate date1,
LocalDate date2,
LocalDate date3,
LocalDate date4
) {
}
}The engine follows a simple and explicit contract:
- child nodes are evaluated in the order they were added
- the first matching result wins
- if no child returns a result, the current node may return its own fallback result
- if nothing matches, the engine returns
DecisionResult.noMatch()
This keeps evaluation deterministic and easy to reason about.
In addition to plain evaluation, the engine can produce an execution trace.
EvaluationReport<String> report = engine.evaluateWithTrace(input);The trace contains visited nodes and shows:
- which nodes were evaluated
- which conditions matched
- which node produced the selected result
This is useful for:
- debugging rule trees
- writing precise tests
- explaining rule behavior to other developers
Represents a boolean check for an input object.
Produces a result value for an input object.
Evaluates the decision tree and returns either a match or noMatch.
Separates two different states:
- no result matched
- a result matched, including cases where the matched value itself is
null
Contains both the evaluation result and the execution trace.
io.github.reloadall.decisiontree
api
builder
engine
support
date
api— public contracts and result modelsbuilder— fluent tree constructionengine— runtime execution modelsupport— helper condition combinatorsdate— reusableLocalDateconditions
Use this library when:
- nested rule logic is growing and becoming hard to read
- ordered rule evaluation matters
- you want reusable tree-shaped decision logic
- you want to explain or trace how a result was selected
This library is probably unnecessary when:
- you only have 2–3 trivial
if-elsechecks - your logic is flat and unlikely to grow
- you need a full business rule management system
- rules must be authored dynamically by non-developers
- you need expression parsing, YAML/JSON rule loading, or runtime configuration
decision-tree-engine is intentionally small. It is designed as a lightweight code-level decision engine, not as a replacement for a full BRMS.
- keep the API small and predictable
- prefer explicit behavior over magic
- avoid premature framework complexity
- provide enough traceability for real debugging
- stay lightweight and easy to embed into ordinary Java code
Included in the current version:
- generic decision tree engine
- fluent builder
- execution trace
- condition combinators
- date comparison helpers
Not included by design:
- Spring integration
- YAML / JSON rules
- expression language
- annotation-driven configuration
- visual rule editor
The project includes tests for:
- root-level branch ordering
- nested branch evaluation
- parent fallback behavior
- no-match scenarios
- execution trace correctness
- condition combinators
- date helper behavior
Possible future improvements:
- more built-in helper conditions
- more expressive builder ergonomics
- Mermaid tree export
- optional validation utilities
The current version already aims to be complete enough for practical use in code.
MIT