This repository shows how to model policy decisions in DMN and execute them in Java using jDMN.
- A realistic loan-approval decision table (
FIRSThit policy). - Additional hit policies (
UNIQUE,ANY,COLLECT + SUM). - Non-table DMN constructs (literal, context, BKM, DRG chaining, relation, list iteration, types, authority requirement).
- A Maven flow where DMN models are converted to Java and validated by tests.
- Loan underwriting is policy-driven logic, which maps directly to decision tables and FEEL expressions.
- Business/risk users can review decision logic in a DMN modeler without reading Java code.
- Rule changes are safer: update model -> regenerate code -> run tests.
- Behavior is auditable: hit policy and rule ordering are explicit in the model.
- DMN is a portable standard, not a proprietary rule format.
DMN is usually a strong fit when your problem looks like business policy evaluation rather than algorithm-heavy computation.
Good fit patterns:
- Policy and eligibility decisions:
- loan approval, insurance underwriting, claims routing, KYC/AML checks, pricing tiers, discount eligibility.
- Rule logic changes often:
- thresholds/conditions are updated by business teams and must be shipped safely.
- Explainability and auditability are required:
- you need to answer "which rule fired and why?" for regulators, audits, or customer support.
- Multiple teams collaborate on rules:
- analysts define policy, developers integrate execution and tests.
- Decisions are composable:
- you benefit from DRG-style chaining (one decision feeding another) and reusable BKMs.
Usually not a great fit:
- Heavy numerical/ML workloads:
- optimization, large-scale forecasting, vector math, model training/inference pipelines.
- Highly procedural workflows:
- long-running orchestration with retries/timeouts/state machines (better handled by workflow engines).
- Low-value static logic:
- simple code branches that rarely change may be clearer as plain application code.
mvn clean testRun demos:
mvn -q exec:java -Dexec.mainClass=com.example.jdmn.demo.DemoMain
mvn -q exec:java -Dexec.mainClass=com.example.jdmn.demo.HitPolicyDemoMain
mvn -q exec:java -Dexec.mainClass=com.example.jdmn.demo.DmnConceptDemoMain
mvn -q exec:java -Dexec.mainClass=com.example.jdmn.demo.LoanAuditDemoMainRun the local editor server:
mvn -q exec:java -Dexec.mainClass=com.example.jdmn.editor.DmnEditorServerThen open: http://127.0.0.1:8090
What it supports:
- Visual DMN editing in browser (
dmn-jsmodeler). - View switcher (DRD / Decision Table / Literal Expression) when multiple views exist.
- Auto-opens a non-DRD view first (useful when DRD layout metadata is missing).
- XML mode toggle for direct source editing when canvas view is not helpful.
- List/open/save
.dmnfiles fromsrc/main/resources/dmn. - Create a new
.dmnfile from current canvas. - Run
mvn clean testfrom the UI and view output.
Notes:
dmn-jsis loaded from CDN (unpkg), so internet access is required for the editor UI assets.- Backend only allows root-level
.dmnfiles undersrc/main/resources/dmn(no nested paths).
JdmnLoanApprovalEngine has two evaluation modes:
evaluate(...)returns onlyLoanDecision.evaluateWithAudit(...)returnsLoanDecisionAuditwith:- decision metadata (
decisionName,hitPolicy) - input snapshot (
inputs) - all evaluated rules (
evaluatedRules) - matched rules (
matchedRules) - per-column condition results (
columnChecks)
- decision metadata (
This gives you direct answers to:
- Which rule fired?
audit.matchedRules().get(0).ruleIndex()and.annotation()
- Why did it fire (or not fire)?
audit.evaluatedRules()and each rule'scolumnChecksvalues (true/false).
| DMN file | Category | Why this example exists |
|---|---|---|
src/main/resources/dmn/loan-approval.dmn |
Decision table (FIRST) |
Shows realistic underwriting where rule order determines the final outcome. |
src/main/resources/dmn/shipping-fee-unique.dmn |
Hit policy UNIQUE |
Shows cases where exactly one rule must match to keep outputs deterministic. |
src/main/resources/dmn/transaction-action-any.dmn |
Hit policy ANY |
Shows multiple matching rules are allowed only when they agree on the same output. |
src/main/resources/dmn/insurance-surcharge-collect-sum.dmn |
Hit policy COLLECT + SUM |
Shows independent matched rules being aggregated into a total surcharge. |
src/main/resources/dmn/literal-expression.dmn |
Literal expression | Smallest DMN decision: formula-only, no table. |
src/main/resources/dmn/context-discount.dmn |
Context | Demonstrates named intermediate values for readable calculations. |
src/main/resources/dmn/bkm-invocation.dmn |
BKM + invocation | Demonstrates reusable business functions called by decisions. |
src/main/resources/dmn/drg-chained-decision.dmn |
DRG chaining | Demonstrates decision decomposition (one decision feeds another). |
src/main/resources/dmn/relation-catalog.dmn |
Relation | Demonstrates in-model tabular data consumed by another decision. |
src/main/resources/dmn/list-iteration.dmn |
FEEL list/for/sum | Demonstrates iteration and aggregation inside DMN expressions. |
src/main/resources/dmn/type-system.dmn |
Item definitions | Demonstrates explicit domain types/type aliases in DMN. |
src/main/resources/dmn/authority-requirement.dmn |
Governance metadata | Demonstrates ownership/authority traceability around decisions. |
src/main/java/com/example/jdmn/loan/LoanApplication.javasrc/main/java/com/example/jdmn/loan/LoanDecision.javasrc/main/java/com/example/jdmn/loan/LoanDecisionAudit.javasrc/main/java/com/example/jdmn/loan/JdmnLoanApprovalEngine.javasrc/test/java/com/example/jdmn/loan/JdmnLoanApprovalEngineTest.java
src/main/java/com/example/jdmn/runtime/JdmnExecutionContextFactory.javasrc/main/java/com/example/jdmn/support/DecisionNumbers.javasrc/main/java/com/gs/dmn/runtime/ContextImpl.java(compatibility shim for relation code generation)src/test/java/com/example/jdmn/testutil/BigDecimalAssertions.java
src/main/java/com/example/jdmn/editor/DmnEditorServer.javasrc/main/resources/editor/index.htmlsrc/main/resources/editor/app.jssrc/main/resources/editor/styles.css
- Hit policy wrappers/tests:
src/main/java/com/example/jdmn/examples/hitpolicy/HitPolicyExamples.javasrc/test/java/com/example/jdmn/examples/hitpolicy/HitPolicyExamplesTest.java
- Non-table concept wrappers/tests:
src/main/java/com/example/jdmn/examples/concepts/DmnConceptExamples.javasrc/test/java/com/example/jdmn/examples/concepts/DmnConceptExamplesTest.java
- Runnable demos:
src/main/java/com/example/jdmn/demo/DemoMain.javasrc/main/java/com/example/jdmn/demo/HitPolicyDemoMain.javasrc/main/java/com/example/jdmn/demo/DmnConceptDemoMain.javasrc/main/java/com/example/jdmn/demo/LoanAuditDemoMain.java
- DMN models under
src/main/resources/dmnare transformed to Java byjdmn-maven-pluginduringgenerate-sources. - Generated classes are added to compilation via
build-helper-maven-plugin. - Tests validate expected decision behavior and document why each output is expected.
DMN files are XML on disk, but they are normally authored in a DMN modeler UI (decision table grid/diagram), not by hand.
Suggested collaboration loop:
- Analysts edit in DMN modeler and export
.dmn. - Developers commit updated DMN under
src/main/resources/dmn. - CI/local run
mvn clean testto validate generated code and expected outcomes.