Skip to content

tiana-code/decision-engine

Repository files navigation

Decision Engine

A deterministic, embeddable rules engine for the JVM. Rules are a typed JSON predicate DSL; evaluation is pure, auditable, and bounded in time. No embedded scripting language, no surprises.

License: BUSL-1.1 Build Java 21 Kotlin


What it is

A small, dependency-light decision engine: you express a business rule as JSON, parse it once, and evaluate it against typed inputs as often as you like. Every evaluation is a pure function of the rule and the input, so the same facts always yield the same outcome. That makes decisions reproducible, testable, and safe to record in an audit log.

It is built for the cases where a full business-rules platform is too much and hand-written if trees are too little: pricing gates, eligibility checks, fraud and AML screening, feature flags with real predicates, and anywhere a non-deterministic or script-backed rule would be a liability.

What it is not: a workflow engine, a scripting sandbox, or a DMN suite. There is no embedded language to inject into, and no I/O during evaluation.


Why it is different

  • Deterministic by construction. Evaluation has no clock, no randomness, no I/O. Identical input gives identical output, every time. A property test pins this.
  • Fail-closed parsing. A malformed or unknown rule is rejected at parse time with a typed error, never silently treated as "match" or "no match".
  • Bounded-time matching. The matches operator runs user-supplied regular expressions against an interruptible input view under a watchdog, so a catastrophic backtrack (ReDoS) unwinds promptly instead of hanging the caller.
  • No scripting language. Rules are data, not code. There is nothing to eval, so there is no injection surface.
  • Embeddable. One small library, the only runtime dependency is Jackson. Parse and evaluate in-process; no service, no framework required.

Quick start

import com.fincore.decision.domain.DecimalValue
import com.fincore.decision.domain.EvaluationInput
import com.fincore.decision.eval.RuleEvaluator
import com.fincore.decision.parser.RuleParser
import java.math.BigDecimal

val parser = RuleParser()
val evaluator = RuleEvaluator()

val rule = parser.parse(
    """{"condition":{"attr":"age","op":"gte","value":18},"outcome":{"label":"APPROVE"}}""",
)

val input = EvaluationInput(mapOf("age" to DecimalValue(BigDecimal("20"))))
val result = evaluator.evaluate(rule, input)

result.matched          // true
result.outcome?.label   // "APPROVE"

Parse a rule once and reuse it; both RuleParser and RuleEvaluator are stateless and safe to share.


The DSL

A rule is a JSON object with a condition and an outcome:

{
  "condition": {
    "all": [
      { "attr": "age", "op": "gte", "value": 18 },
      { "attr": "country", "op": "in", "value": ["DE", "FR", "ES"] },
      { "attr": "email", "op": "matches", "value": "^[^@]+@[^@]+$" }
    ]
  },
  "outcome": { "label": "APPROVE" }
}

Comparison operators: eq, neq, lt, lte, gt, gte, in, not_in, and matches (bounded-time regular expression).

Logical groups: all, any, none, nestable to any depth.

Value types: string, decimal (BigDecimal, no floating-point drift), and boolean. Inputs are typed, so a decimal attribute is compared numerically and a string attribute lexically, with no implicit coercion.


Rule synthesis port

For teams generating rules from natural language, the engine ships a RuleSynthesizer interface in com.fincore.decision.synth: a clean contract for turning a prompt into a candidate rule, with no implementation and no LLM dependency in this repository. The candidate it returns is untrusted by design; the caller validates it through the same fail-closed RuleParser before use. Bring your own model behind the port.


Build

Requires JDK 21.

./gradlew build      # compile, test, lint
./gradlew test       # run the test suite

Published artifacts are not on a public registry yet. Until then, build from source, or consume the module inside FinCore Engine, where this engine originated.


License

Business Source License 1.1 (BUSL-1.1). Free for non-production use, evaluation, education, and contributions; it converts to Apache 2.0 on the change date in LICENSE. Production use before then requires a commercial license.


About

Extracted from FinCore Engine, an open-source financial core for the JVM. Built by Tiana Rogozina, focused on correct, maintainable financial infrastructure.

About

Deterministic, embeddable rules engine for the JVM: a typed JSON predicate DSL with a fail-closed parser and bounded-time evaluator.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages