Strict DMN hit policies
UNIQUE, ANY, PRIORITY, FIRST, OUTPUT ORDER, RULE ORDER, and COLLECT with SUM, MIN and MAX.
Open source · Apache-2.0 · Java 21+
Praecepta (Latin for “rules”, said pry-KEP-ta) evaluates DMN decision tables with strict DMN hit policies. Write tables in a modeller, Excel, CSV or code; validate them when you build; evaluate them with a small engine that has no dependencies.
Latest release: 0.1.0, on Maven Central
record Customer(String country, Integer age) {}
DecisionTable table;
try (var dmn = Files.newInputStream(Path.of("discount.dmn"))) {
table = DmnReader.read(dmn);
}
var engine = PraeceptaEngine.<Customer, String>builder()
.table(table) // hit policy, types, value lists, rules
.input("country", Customer::country)
.numberInput("age", Customer::age)
.output(output -> output.values().get("tier"))
.build();
engine.evaluate(new Customer("PT", 30)); // [gold]
engine.validationReport(); // overlaps, unreachable rules, gaps
JMH on an Apple M2 Pro, every engine reading the same DMN file and checked to give the same answers. Every log and raw result is published, including where Praecepta is slower: building large FIRST and PRIORITY tables (#29).
UNIQUE, ANY, PRIORITY, FIRST, OUTPUT ORDER, RULE ORDER, and COLLECT with SUM, MIN and MAX.
Lists, not(...), comparisons, intervals and null on string, number and
date columns, with optional input value lists.
Overlapping rules, conflicting outputs, rules that can never fire, and gaps: inputs no rule covers, with an example.
Reads DMN 1.1 to 1.5, including files from Camunda Modeler, and writes DMN 1.3 that Camunda and Drools evaluate the same way.
Compiles Excel, CSV and DMN tables at build time into typed input and output records, so your application ships only the engine.
No runtime dependencies, a JPMS module, and immutable engines that any number of threads can share.
It has no dependencies. Add praecepta-loader too if you load Excel or CSV files at
runtime.
<dependency>
<groupId>io.github.brunopacheco1</groupId>
<artifactId>praecepta-engine</artifactId>
<version>0.1.0</version>
</dependency>
Read a DMN file as in the example above, or build the rules in code. Bind each column to a getter of your input, and map the matched outputs to your result.
The Maven plugin validates each table, fails the build on errors, and generates typed classes:
<plugin>
<groupId>io.github.brunopacheco1</groupId>
<artifactId>praecepta-maven-plugin</artifactId>
<version>0.1.0</version>
<executions><execution><goals><goal>generate</goal></goals></execution></executions>
<configuration>
<packageName>com.acme.pricing</packageName>
<tables>
<table><source>src/main/rules/discount.dmn</source></table>
</tables>
</configuration>
</plugin>
Optional<DiscountOutput> discount =
Discount.evaluate(new DiscountInput(DiscountInput.Country.PT, new BigDecimal(30)));
The README covers cells, typed columns, DMN support and every plugin option. The API documentation covers the engine and the loader.
Every rule gets one bit. For every column, each value the rules can tell apart gets a mask: the rules that accept it. Numbers and dates are split at the boundaries the rules mention, and a binary search finds the interval.
Evaluating an input looks up one mask per column and ANDs them. A 64-bit AND checks 64 rules at once, and the hit policy reads the result: FIRST is the lowest set bit, a single CPU instruction. Memory grows with columns × values × rules, never with their product.
| Rule | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| country | "PT" | "FR" | - | not("FR") |
| age | [18..65) | - | < 18 | >= 18 |
| country mask | 1 | 0 | 1 | 1 |
| age mask | 1 | 1 | 0 | 1 |
| AND | 1 | 0 | 0 | 1 |
FIRST: rule 1 · COLLECT: rules 1 and 4
Praecepta does one thing: single DMN decision tables, fast. Use Camunda or Drools when you need:
income > 2 * rent. Praecepta cells are FEEL simple unary tests on one
column.The threat model, trust boundaries and evidence are in the security assurance case. To report a vulnerability, see the security policy.
These are intentions, not promises: the roadmap follows what people ask for.
Say which features you would use: a 👍 on an issue is enough. The contributing guide explains how to build and test, and some issues are marked good first issue. Reviewers and a co-maintainer are very welcome: the governance document explains how decisions are made and how to become a maintainer.