Decision Table
A Decision Table is the most common boxed expression in DMN. It represents a rules engine where inputs are matched against rules to determine an output.
Structure
- Hit Policy: Determines how many rules can match and how the result is aggregated.
- Input Clauses: The columns defining conditions (e.g.,
Age,Risk Level). - Output Clauses: The columns defining results (e.g.,
Approved,Interest Rate). - Rules: The rows containing input entries (unary tests) and output entries (expressions).
Hit Policies
| Code | Name | Description |
|---|---|---|
| U | Unique (default) | Overlap is forbidden. At most one rule may match — multiple matches are a runtime error. |
| A | Any | Rules may overlap, but all matching rules must produce the same output. Different outputs across matching rules are a runtime error. |
| P | Priority | Rules may overlap. The output is selected based on the priority of the output values, declared in the output column's allowed-values list (earlier value = higher priority). |
| F | First | Rules are evaluated top-to-bottom. The first matching rule's output is returned, the remaining rules are skipped. |
| C | Collect | All matching outputs returned as a list. Optionally aggregated to a scalar via the aggregation attribute. |
| C+ | Collect (Sum) | Sum of all matching outputs. |
| C< | Collect (Min) | Smallest of all matching outputs. |
| C> | Collect (Max) | Largest of all matching outputs. |
| C# | Collect (Count) | Number of matching rules. |
| R | Rule Order | All matching outputs in rule (top-to-bottom) order, as a list. |
| O | Output Order | All matching outputs sorted by the output's allowed-values list, as a list. |
If no hitPolicy is declared on a decision table, the engine treats it as Unique.
No-match behaviour
When no rule matches:
- If the output column declares a
<defaultOutputEntry>, that default is evaluated and returned. - Otherwise the result is
null(or a context of nulls for compound outputs).
Collect, Rule Order, and Output Order also return null on no-match unless a default is configured — they do not implicitly return an empty list.
Example
| U | Age | Credit Score | Loan Approved |
|---|---|---|---|
| 1 | < 18 | - | false |
| 2 | >= 18 | > 700 | true |
| 3 | >= 18 | <= 700 | false |
FAQ
What is a hit policy in a DMN decision table?
The hit policy tells the engine how many rules are allowed to match and how to combine their outputs when more than one does. It is the single most important attribute of a decision table because it determines the table's semantics under overlap. The DMN spec defines seven hit policies: Unique, Any, Priority, First, Collect, Rule Order, and Output Order — Unique and Any return a single output (with constraints), Priority and First pick one matching rule, and Collect, Rule Order, and Output Order return a list. If you don't declare a hit policy explicitly, the engine treats the table as Unique.
Which DMN hit policies does QuantumBPM support?
All seven DMN 1.5 hit policies are supported: Unique (U), Any (A), Priority (P), First (F), Collect (C) with optional aggregations Sum (C+), Min (C<), Max (C>), and Count (C#), Rule Order (R), and Output Order (O). The XML attribute is hitPolicy on the <decisionTable> element, with aggregation specified by the separate aggregation attribute (SUM, MIN, MAX, COUNT) when hitPolicy="COLLECT".
What's the difference between the Unique and Any hit policies?
Both return a single output. Unique forbids overlap entirely — at runtime, if more than one rule matches, the engine raises an error. Any allows multiple rules to match as long as they all produce the same output values, if they produce different outputs, that's also an error. So Unique is a stricter design-time contract ("these rules can never overlap") while Any is more permissive ("they might overlap, but I guarantee they agree"). Pick Unique when your input domains are designed to partition cleanly with no overlap, pick Any when overlap is acceptable but you want to assert that overlapping rules don't contradict each other.
What's the difference between the First and Priority hit policies?
Both pick exactly one output from multiple matching rules, but they pick differently. First is positional: rules are evaluated top-to-bottom and the first matching rule wins. Priority is data-driven: every output column declares a list of allowed values in priority order (earlier = higher priority), and across all matching rules, the one whose output sits earliest in that list wins. First makes rule order in the table semantically meaningful — reordering rules can change the answer. Priority makes the priority list semantically meaningful — reordering rules has no effect, but reordering the allowed-values list does.
What's the difference between Collect, Rule Order, and Output Order?
All three return a list of outputs when multiple rules match. Collect returns the outputs as a list in no particular order (or aggregated into a scalar via SUM, MIN, MAX, or COUNT when the aggregation attribute is set). Rule Order returns outputs in the order the matching rules appear in the table (top-to-bottom). Output Order returns outputs sorted by their values according to the output column's allowed-values list. Use Collect when order does not matter (especially with an aggregation), Rule Order when the table's row order is meaningful as a sort key, and Output Order when you want to sort by output value (e.g., "return all matching tiers ordered from highest to lowest").
What does the Collect hit policy's aggregation do?
With hitPolicy="COLLECT", the optional aggregation attribute reduces the matched outputs to a single scalar instead of returning a list. SUM adds all matched outputs (numbers only — non-numeric values are skipped). MIN and MAX return the smallest and largest matched output respectively. COUNT returns the number of matching rules (regardless of the output values). Without an aggregation attribute, Collect returns the raw list of matched outputs. This is useful for tables like "compute a total fee by collecting all matching fee rules and summing them" — Collect + SUM in one table.
What happens when no rule matches in a DMN decision table?
The engine first checks each output column for a <defaultOutputEntry>. If at least one column declares a default, the engine evaluates each default expression and returns the resulting values as the table's output. If no default outputs are configured, the result is null (or a context whose entries are all null for a compound-output table). This applies uniformly to every hit policy — Collect, Rule Order, and Output Order also return null on no-match unless a default is configured, they do not implicitly return an empty list. The recommendation is to declare default outputs explicitly whenever a no-match scenario is meaningful in your domain — that makes the behaviour visible in the table rather than a runtime null surprising a downstream consumer.
How is a decision-table input entry different from a regular FEEL expression?
Input entries are unary tests — a syntactic subset of FEEL designed to be evaluated against an implicit input value. Instead of writing Age >= 18, you write >= 18 in the Age column, the engine compares the rule's input value against that test. Unary tests support comparisons (< 10, >= 0), intervals ([1..10], (1..10)), lists (1, 2, 3 meaning "any of these"), negation (not(0)), and the literal - (a hyphen) which means "any value" (the rule does not constrain that column). The output entries, by contrast, are full FEEL expressions — they can call functions, do arithmetic, build contexts, anything FEEL supports.
What's the difference between a single-output and a compound-output decision table?
A single-output table has one output column. The result of evaluating the table is just the value of that output (a number, string, boolean, whatever). A compound-output table has two or more output columns, and the result is a context — a key-value object whose keys are the output column names and whose values are the per-column output entries from the matching rule. Compound outputs are useful when a decision naturally returns several related values together (e.g., a loan decision returning both approved: boolean and interestRate: number), single-output tables are simpler and easier to consume from FEEL.
Are decision-table rules evaluated in order, or in parallel?
Conceptually, every rule is evaluated against the inputs — the hit policy then decides what to do with the matches. The only hit policy where evaluation order is observable is First, which short-circuits as soon as a matching rule is found (so rules below the first match are never evaluated). For every other hit policy, the full set of matching rules has to be determined before the policy can be applied, so rule order in the table only matters as documentation. The engine evaluates rules left-to-right within a rule (each input entry against the corresponding input) and conceptually top-to-bottom across rules, but you should not rely on a particular evaluation order for side effects — FEEL is side-effect free anyway.
How does the Output Order hit policy actually sort the results?
Output Order sorts the matched rules by their output values, using each output column's declared allowed-values list as the sort key. Within an output column, an output that appears earlier in the allowed-values list ranks higher (gets sorted first) than an output that appears later. When two rules have the same value in the first output column, the next output column is used as a tiebreaker, and so on. The classic use case is a list of approval levels ("platinum", "gold", "silver", "bronze" as the priority order) where you want all matching tiers returned in priority order so the caller can pick the best one. If no allowed-values list is declared on the column, the sort is effectively a no-op for that column.
What does the validator catch in a DMN decision table?
At deploy time the modeler and the engine catch shape errors like missing hit policies (defaults to Unique), invalid aggregation values for non-Collect tables, references to unknown input names, FEEL parse errors in input or output entries, and inconsistent rule shapes (e.g., a rule with fewer input entries than the table has input columns). At evaluation time the engine catches semantic violations of the hit policy: more than one rule matching a Unique table, or matching rules with different outputs under Any. Other policies (First, Priority, Collect, Rule Order, Output Order) tolerate any number of matches and so do not raise a hit-policy violation. The recommendation is to enable strict modeling — declare allowed values for outputs, use Unique by default, and reserve First/Priority for cases where overlap is genuinely part of the design.