DMN Elements

Decision Model and Notation (DMN) allows you to externalize business rules from BPMN processes into separate, version-controlled decision tables. Priostack supports DMN 1.3 decision tables: input entries are unary tests, and output entries are FEEL expressions evaluated by the engine's FEEL implementation (see the FEEL Reference for what it covers).

Decision Table Structure

A decision table consists of:

Hit Policies

PolicyCodeDescription
UNIQUE U Only one rule may match. If multiple rules match, evaluation throws an error. Use when rules are mutually exclusive.
FIRST F The first matching rule (in row order) is returned. Rules are evaluated top to bottom; once a match is found, evaluation stops.
RULE ORDER R All matching rules are returned in the order they appear in the table. Output is a list.
COLLECT C All matching rules are returned as a list (order not guaranteed). Can be combined with aggregators: C+ (sum), C< (min), C> (max), C# (count).
ANY A Multiple rules may match, but all matching rules must produce the same output. Returns a single result.
OUTPUT ORDER O All matching rules are returned, ranked by the first output's declared output values (outputValues), most preferred first. Without declared values they stay in rule order.
PRIORITY P The single matching rule whose first output ranks highest in that output's declared output values.

POST /api/v1/decisions/{decisionId}/evaluate returns one row: {"result":{...}} holds the first row of the result, with output values as strings. For RULE ORDER, OUTPUT ORDER and COLLECT without an aggregator, the other matching rows are not returned by that call.

Example: Order Discount Decision

The following decision table determines the discount percentage based on customer tier and order amount:

Order Discount (Hit Policy: FIRST)
Rule Customer Tier (Input) Order Amount (Input) Discount % (Output)
1"gold"> 50015
2"gold"[100..500]10
3"silver"> 2008
4"silver"< 2005
5--0

DMN XML for the above table

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/"
             namespace="http://priostack.com"
             name="Order Discount"
             id="orderDiscount">
  <decision id="discount" name="Order Discount">
    <decisionTable id="decisionTable" hitPolicy="FIRST">
      <input id="input1" label="Customer Tier">
        <inputExpression id="inputExpr1" typeRef="string">
          <text>customerTier</text>
        </inputExpression>
      </input>
      <input id="input2" label="Order Amount">
        <inputExpression id="inputExpr2" typeRef="number">
          <text>orderAmount</text>
        </inputExpression>
      </input>
      <output id="output1" label="Discount %" name="discountPercent" typeRef="number" />
      <rule id="rule1">
        <inputEntry id="in1"><text>"gold"</text></inputEntry>
        <inputEntry id="in2"><text>> 500</text></inputEntry>
        <outputEntry id="out1"><text>15</text></outputEntry>
      </rule>
      <rule id="rule2">
        <inputEntry id="in3"><text>"gold"</text></inputEntry>
        <inputEntry id="in4"><text>[100..500]</text></inputEntry>
        <outputEntry id="out2"><text>10</text></outputEntry>
      </rule>
      <rule id="rule3">
        <inputEntry id="in5"><text>"silver"</text></inputEntry>
        <inputEntry id="in6"><text>> 200</text></inputEntry>
        <outputEntry id="out3"><text>8</text></outputEntry>
      </rule>
      <rule id="rule4">
        <inputEntry id="in7"><text>"silver"</text></inputEntry>
        <inputEntry id="in8"><text>&lt; 200</text></inputEntry>
        <outputEntry id="out4"><text>5</text></outputEntry>
      </rule>
      <rule id="rule5">
        <inputEntry id="in9"><text></text></inputEntry>
        <inputEntry id="in10"><text></text></inputEntry>
        <outputEntry id="out5"><text>0</text></outputEntry>
      </rule>
    </decisionTable>
  </decision>
</definitions>

Deploy it with POST /api/v1/models, then evaluate. Each row of the table has a case with the answer the engine gives (FIRST hit policy):

curl -X POST https://priostack.com/api/v1/decisions/discount/evaluate \
  -H "X-API-Key: $PRIOSTACK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"customerTier":"silver","orderAmount":300}'
# {"result":{"discountPercent":"8"}}

# gold / 600   -> "15" (rule 1)
# gold / 500   -> "10" (rule 2)
# silver / 300 -> "8"  (rule 3)
# silver / 100 -> "5"  (rule 4)
# silver / 200 -> "0"  (rule 5: 200 is neither > 200 nor < 200)
# gold / 50    -> "0"  (rule 5)

Input Expressions

Input expressions are FEEL expressions evaluated against the current process variables. The result is compared to each rule's input entry.

Input Entry SyntaxMeaning
"gold"Exact string match
> 500Greater than 500
[100..500]Between 100 and 500 (inclusive)
"gold", "platinum"One of these values
not("cancelled")Any value except "cancelled"
< date("2026-01-01")Before that date
< dueDateLess than the value of the variable dueDate
(empty) or -Matches any value (wildcard)

A cell is a unary test against its column's input expression, not a full boolean expression. The deploy refuses (422) a cell such as status in ["pending", "review"] or dueDate < today(); even < today() is refused, because a function call is not a readable endpoint.

Referencing DMN from BPMN

Use a Business Rule Task in your BPMN to invoke a DMN decision. The engine evaluates it inline; no worker is involved. This snippet is a fragment for a document that declares xmlns:zeebe="http://camunda.org/schema/zeebe/1.0".

<businessRuleTask id="applyDiscount" name="Calculate Discount">
  <extensionElements>
    <zeebe:calledDecision decisionId="discount" />
  </extensionElements>
</businessRuleTask>

decisionId names the decision; a resultVariable attribute is not read. The decision's outputs are written into the process under their own output names: after this task, the process variable discountPercent holds "10" (a string). Reference it in later FEEL expressions as discountPercent.

Tip: Use the DMN Editor to author decision tables visually. Deploy DMN files through POST /api/v1/models, which detects the model kind (1 credit, refunded if the deploy fails); POST /api/v1/process-definitions deploys BPMN only and answers 422 for a DMN file. Deploy the decision before any process that calls it: a business rule task whose decision is missing puts the instance in INCIDENT.

FEEL in Decision Conditions

Input entries are unary tests; output entries and input expressions may be full expressions. When a condition needs a function, put the function in the column's input expression and let the cell test its result:

<!-- Input entry (column: status): one of several values -->
<inputEntry id="ie1"><text>"pending", "review"</text></inputEntry>

<!-- Output entry: compute a value -->
<outputEntry id="oe1"><text>amount * 0.1</text></outputEntry>

<!-- Date comparison: the function goes in the input expression... -->
<input id="overdue" label="Overdue">
  <inputExpression id="overdueExpr" typeRef="boolean">
    <text>date(dueDate) &lt; today()</text>
  </inputExpression>
</input>
<!-- ...and the cell tests its result -->
<inputEntry id="ie2"><text>true</text></inputEntry>

For a complete FEEL reference, see the FEEL Reference page.