Lightweight rule engine
Digital Twin includes a lightweight, configurable rule engine that evaluates each incoming transaction against declarative rules. It is deliberately minimal — single-pass and stateless: it decides whether to authorize or decline a transaction and which balances the entry affects. It is not an inference / production-rule system (no chaining, no working memory) — and that simplicity is intentional (see references).
How a rule matches
Rules are configuration (reference data), not code. Each rule holds ordered patterns; a pattern fires when all its criteria match the transaction. Criteria match on transaction and account attributes, or on a small predicate expression language (DSL):
| Criterion | Matches against | Example |
|---|---|---|
PHASE | transaction phase | PENDING, POSTING, * |
OPERATION_KIND | debit or credit | DEBIT, CREDIT |
HISTORY_CODE | transaction code(s) | 1;2;173 or * |
ACCOUNT_TYPE | account type(s) | 1;2;3 or * |
ENTRY_BASED | entry / metadata fields | DSL expression ↓ |
# each expression is a single condition that evaluates to true/false
account().feature('SPI_CATEGORY').isNotEqualTo(metaData('$.account.category'))
metaData('$.holder.taxId').containsNoneOf(account().owners())
Composition is structural: multiple criteria in a pattern are ANDed; multiple patterns act as alternatives, evaluated in priority order (first match wins).
Routing balances: a fast matrix, with a break-glass DSL
Which balances a transaction affects is decided by the same engine, driven overwhelmingly by a fast token matrix — HISTORY_CODE × OPERATION_KIND × PHASE × ACCOUNT_TYPE × BLOCKING_KIND × HOLD_REASON → the set of balance types. Rules are cached in memory and matched by simple value comparison, so this stays cheap on the hot path.
The DSL is the break-glass option: when a flat token match can’t express the routing, a balance rule can add a small expression instead. It’s used deliberately and rarely — a per-transaction expression costs more than a value compare — so the matrix stays the default. Real examples in use:
# route by the transaction code's DOCUMENT_TYPE feature
feature('DOCUMENT_TYPE').isEqualToAnyOf('30', '31', '29')
# route by whether the hold reason permits overdraft usage
feature('ALLOW_OVERDRAFT_LIMIT_USAGE').isFalse()
What it is — and what it isn’t
References
The scope and boundaries above follow standard, published definitions:
Martin Fowler, “RulesEngine” (martinfowler.com, 2009) — a rules engine may be as simple as a collection of condition/action rules; chaining is the powerful-but-risky feature (“very hard to reason about and debug”). martinfowler.com/bliki/RulesEngine.html
Charles L. Forgy, “Rete: A Fast Algorithm for the Many Pattern/Many Object Pattern Match Problem” (Artificial Intelligence, 1982) — the inference-engine algorithm behind full production-rule systems (which this deliberately is not).
OMG, Decision Model and Notation (DMN) — the standard for modeling business decisions/rules; the reference point for a full BRMS.