决策模型和标记DMN(Decision Model and Notation),是一个商业决策管理标准。
当前camunda的DMN引擎部分支持DMN1.3标准,包括决策表(Decision Tables),决策字面量表达式(Decision Literal Expressions),决策需求图(Decision Requirements Graphs)和足够友好表达语言FEEL(Friendly Enough Expression Language )
决策表(Decision Table):将决策逻辑指定为表
决策字面量表达式(Decision Literal Expression):将决策逻辑指定为表达式
决策需求图(Decision Requirements Graph):为相互依赖的决策建模
足够友好表达语言FEEL(Friendly Enough Expression Language):DMN的默认表达式语言
一、DMN决策表(DMN Decision Table):
决策表表示一个在DMN1.3中能够被描述成一个表格的决策逻辑。它包含输入(inputs),输出(outputs)和规则(rules)
决策表在xml中用一个decision元素中的decisionTable元素表示:
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
<decision id="dish" name="Dish">
<decisionTable id="decisionTable">
<!-- ... -->
</decisionTable>
</decision>
</definitions>
决策名称描述了这个决策表提供的决策逻辑。它被设置成一个name属性在decision元素上。
<decision id="dish" name="Dish">
<decisionTable id="decisionTable">
<!-- ... -->
</decisionTable>
</decision>
1.决策表名称和编号(Decision Name / Decision Id):
决策表名称(Decision Table Name)描述了这个决策表提供的决策逻辑。它被设置成一个name属性在decision元素上。
<decision id="dish" name="Dish">
<decisionTable id="decisionTable">
<!-- ... -->
</decisionTable>
</decision>
决策表编号(Decision Id)是技术上的标识。它设置在decision元素的id属性上。每个决策必须要有唯一的id,当在部署到camunda BPM平台上时。引擎会将id用作部署决策定义的决策key。
<decision id="dish" name="Dish">
<decisionTable id="decisionTable">
<!-- ... -->
</decisionTable>
</decision>
2.DMN决策表输入(DMN Decision Table Input):
一个决策表有一个或者多个输入,也被叫做输入子句。输入子句定义了编号(id),标签(label),表达式(expression),决策表输入类型(type)
一个input可以通过双击决策表各列的列头来进行编辑。
在xml中输入子句用decisionTable内的input元素表示:
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
<decision id="dish" name="Dish">
<decisionTable id="decisionTable">
<input id="input1" label="Season">
<inputExpression id="inputExpression1" typeRef="string">
<text>season</text>
</inputExpression>
</input>
<!-- ... -->
</decisionTable>
</decision>
</definitions>
2.1.输入编号(Input Id):
输入编号是决策表input的唯一标识。
2.2.输入标签(Input Label):
输入标签是input的一个简短的描述。
2.3.输入表达式(Input Expression):
输入表达式指定输入子句的值如何生成。这是一个会被DMN引擎解析的表达式。它通常很简单并且引用一个执行期可见的变量引用。
2.4.输入类型定义(Input Type Definition):
当输入表达式被DMN引擎解析后,结果会被转换成指定的类型,支持的类型在用户指南中查看。注意:类型不是必须的,但是推荐要设置,因为它可以帮助我们明白可能的输入值,和提供类型安全的校验。
2.5.输入表达式语言(Input Expression Language):
表达式语言可以指定,输入表达式的语言,如果没有指定,则会使用definitions元素中的全局表达式语言。如果全局表达式语言也没有设置,则默认使用JUEL表达式语言。
2.6.输入变量名称(Input Variable Name):
当输入表达式被解析并返回解析的值时,值会被存储到一个变量中,这个变量的名称就是输入变量名称。这个变量名还能使用表达式。
3.决策表输出(DMN Decision Table Output):
决策表可以有一个或者多个输出(outputs),也被叫做输出子句。输出子句定义了编号(id),标签(label),名称(name),决策表输出类型(type)。
在xml中输入子句用decisionTable内的output元素表示。
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
<decision id="dish" name="Dish">
<decisionTable id="decisionTable">
<!-- ... -->
<output id="output1" label="Dish" name="desiredDish" typeRef="string" />
<!-- ... -->
</decisionTable>
</decision>
</definitions>
3.1.输出编号(Output Id):
输出编号是决策表输出的唯一编号,被camunda平台用来关联被执行的决策的历史输出。
3.2.输出标签(Output Label):
输出标签时一个输出的简短描述。输出标签不是必须的,但是推荐设置,因为他可以帮助我们理解决策。
3.3.输出名称(Output Name):
输出名称引用的是决策表执行结果的输出值。如果决策表有多个输出,那么所有的输出必须由唯一的名字。
3.4.输出类型定义(Output Type Definition):
当输出条目被DMN引擎执行后,将执行的结果转换成这个指定的类型。支持的类型见用户指南.类型不是必须的,但是推荐设置,因为它可以提供一个类型安全的输出值。
另外,这个类型可以用来将输出值转换成另外一个类型,例如将字符串类型的80%转换成浮点数,详细见用户定制数据类型
4.决策表规则(DMN Decision Table Output):
决策表可以有多个规则。每个规则包含多个输入和输出条目。输入条目是条件,输出条目是规则的结果。如果每一个输入条目都满足,那么这个规则就是满足的,那么决策结果就包含这个规则的输出结果
在xml中,用decisionTable元素内的rule元素来表示一个规则:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
<decision id="dish" name="Dish">
<decisionTable id="decisionTable">
<!-- ... -->
<rule id="rule2-950612891-2">
<inputEntry id="inputEntry21">
<text>"Winter"</text>
</inputEntry>
<inputEntry id="inputEntry22">
<text><![CDATA[<= 8]]></text>
</inputEntry>
<outputEntry id="outputEntry2">
<text>"Roastbeef"</text>
</outputEntry>
</rule>
<!-- ... -->
</decisionTable>
</decision>
</definitions>
4.1输入条目(条件)(Input Entry (Condition)):
一个规则可以有一个个或者多个输入条目,这个条目就是这个规则的条件。每一个输入条目在text元素中包含一个表达式,如果这个输入条目满足,则表达式会被解析成true。
<inputEntry id="inputEntry41">
<text>"Spring"</text>
</inputEntry>
4.2空输入条目(Empty Input Entry):
如果某个输入条目对这个规则无关紧要,这个表达式是空的,那么这个表达式一直都是满足的,即被解析为true。
<inputEntry id="inputEntry41">
<text/>
</inputEntry>
如果使用的是FEEL表达式语言,那么空输入条目用-表示,其他的表达式是空。
4.3输入条目的表达式语言(Expression Language of an Input Entry):
表达式语言可以用expressionLanguage属性指定。支持的表达式语言见用户指南,如果没有设置表达式语言,就会使用设置在definitions中的全局表达式语言。如果全局表达式语言也没有设置,则会使用默认的FEEL表达式语言。
4.4输出条目(结果)(Output Entry (Conclusion)):
一个规则可以有多个输出条目,这个条目是规则的结果。每一个输出条目的text元素中包含一个表达式。
<outputEntry id="outputEntry4">
<text>"Steak"</text>
</outputEntry>
4.5空输出条目(Empty Output Entry):
如果输出条目是空,那么输出会被忽略,并且不是决策表结果的一部分
4.6输出条目表达式(Expression Language of an Output Entry):
见输入条目表达式
4.7描述(Description):
见输出条目描述
5.DMN命中策略(DMN Hit Policy):
决策表的命中策略指定决策表的结果由哪些组成。
命中策略设置在decisionTable元素的hitPolicy属性中。如果没有设置,则默认是UNIQUE命中策略。
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
<decision id="dish" name="Dish">
<decisionTable id="decisionTable" hitPolicy="RULE ORDER">
<!-- .. -->
</decisionTable>
</decision>
</definitions>
5.1命中策略的角色(The Role of a Hit Policy):
命中策略指定决策表的多少个规则可以满足,和哪些满足的规则会被包含到决策表结果中。unique, any 和 first命中策略总是会返回一个最满足的一个规则。rule order 和 collect命中策略会返回多个满足的规则。
5.2唯一命中策略(Unique Hit Policy):
只有一个或者没有规则可以满足。决策表的结果包含满足规则的输出条目。
如果超过一个规则满足,那么就违反了唯一命中策略。
5.3任何一个命中策略(Any Hit Policy):
多个规则可以满足,然后所有满足的规则都必须有相同的输出。这个决策表的结果只会包含一个满足规则的输出。
如果多个规则满足,并且生成了不同的输出,这就违反了命中策略。
这是一个请假的决策表。如果申请这没有假期,或者申请者正在试用期,申请会被拒绝。否则申请通过。
5.4第一个命中策略(First Hit Policy):
多个规则可以满足。决策表结果只包含第一个满足规则的输出。
参见上面的广告决策表。关于根据用户的当前年纪决定哪个广告应该被展示。例如,用户当前是19岁。所有的结果都匹配,但是因为这个命中策略是第一个命中策略,所以Cars广告将会被展示。
5.5规则顺序命中策略(Rule Order Hit Policy):
多个规则可以满足。决策表包含所有满足规则的输出,且按照规则在决策表中的顺序排序。
再看上面关于广告的决策,假设我们有一19岁的用户。所有的规则都满足,所以所有的输出都会按照规则的顺序给到决策表结果。它可能可以用来指示所显示广告的优先级。
5.6集合命中策略(Collect Hit Policy):
多个结果满足。决策表以任意顺序包含所有满足条件的输出结果。
这个命中策略,输出列表是没有顺序的。所以广告可以是任意的,如果年纪是19岁。
另外,集合命中策略可以指定一个聚合器(aggregator)。如果指定了聚合器,决策表的结果只会包含一个输出条目。聚合器会从所有的满足规则的输入条目生成这个输出条目。注意,集合命中策略使用了聚合器,决策表只能有一个输出。
5.7集合命中策略的聚合器(Aggregators for Collect Hit Policy):
集合命中策略还可以指定一个聚合器:
Collect(Sum):所有输出值的求和
Collect(Min):所有输出值中,最小的一个
Collect(Max):所有输出值中,最大的一个
Collect(Count):输出值的个数的统计
5.8求和聚合器(SUM aggregator):
求和聚合器将所有满足的规则的输出求和
这个展示的决策表可以用作对一个员工的奖金进行求和,例如这个员工已经在公司工作了3.5年了。所以会满足第一、二、三个规则,并且这个决策表的结果就是600,所有的输出的求和。
5.9最小值聚合器(MIN aggregator):
最小值聚合器可以用作返回所有满足规则的最小输出值。看下图的汽车保险的例子。一个汽车多年没有发生事故,保险费用应该减少。
例如,如果输入值是3.5年,那么结果应该是98.83,尽管第一、二、三个规则都匹配,但是第三个规则有最小的输出值。
5.10最大值聚合器(MAX aggregator):
最大值聚合器可以被用作返回所有满足规则的最大的输出值。
这个决策表表示小孩子的零花钱的数量的决策。根据小孩子的年纪增加,零花钱的数量会增加。例如,输入一个9,第一、二个规则会匹配到,第二个规则的输出比第一个规则的输出大,所以这个决策的输出是5.一个9岁的小孩子会得到5块钱的零花钱。
5.11计数聚合器(COUNT aggregator):
计数聚合器会返回满足的条件的个数
例如,上图的奖金决策表,如果使用的是计数聚合器,且输入值是4,第一、二、三个规则满足,所以,这个决策表的结果是3,也就是说4年后决策表的结果是3个工资奖金。
二、DMN决策文字表达式(DMN Decision Literal Expression):
一个决策文字表达式表示决策逻辑可以被描述成一个DMN1.3的表达式。它由一个文字表达式和一个变量组成
在xml中,一个决策文字表达式可以用decision元素内的literalExpression元素表示:
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn">
<decision id="season" name="Season">
<variable name="season" typeRef="string" />
<literalExpression>
<text>calendar.getSeason(date)</text>
</literalExpression>
</decision>
</definitions>
1.1决策名称和决策编号(Decision Name / Decision Id):
名称描述了这个文字表达提供的决策逻辑。它设置在decision元素的name属性中:
<decision id="season" name="Season">
<!-- ... -->
</decision>
这个编号是决策的技术编号,它设置在decision元素的id属性中。
在部署到camunda BPM平台时,每一个决策应该有单独的编号。在部署决策定义时,这个编号被引擎用作决策的key
<decision id="season" name="Season">
<!-- ... -->
</decision>
1.2文字表达式(Literal Expression):
这个文字表达式指定了决策的值是如何生成的。这是一个会被DMN引擎执行的表达式。它可以被用作复杂的计算,去调用一个提供了决策逻辑的bean,或者组合输出需求决策的值
表达式被设置在xml中literalExpression元素内的text元素中。
<literalExpression>
<text>calendar.getSeason(date)</text>
</literalExpression>
1.3文字表达式语言(Literal Expression Language):
可以通过literalExpression的expressionLanguage属性指定文字表达式的表达式语言。支持的表达式语言详见用户指南
<literalExpression expressionLanguage="groovy">
<text>calendar.getSeason(date)</text>
</literalExpression>
如果没有设置表达式语言,会使用设置在definitions元素上的全局表达式语言。
<definitions id="definitions"
name="definitions"
xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/"
expressionLanguage="groovy"
namespace="http://camunda.org/schema/1.0/dmn">
<!-- ... -->
</definitions>
如果全局表达式语言也没有设置,会使用默认的表达式语言JUEL,JUEL详见用户指南
1.4变量(Variable):
一个文字表达式必须由一个变量(variable),且指定了name属性和type属性作为决策的结果。变量声明在decision元素内的variable元素中。
<decision id="season" name="Season">
<variable name="season" />
</decision>
1.5变量名称(Variable Name):
变量名称用来引用一个文字表达式的决策结果。它在xml中用name属性指定:
<variable name="season" />
1.6变量类型定义(Variable Type Definition):
在variable元素中,决策结果的类型可以用typeRef属性指定。在决策表达式被执行后,会转换结果成指定的类型。支持的类型详见用户指南
<variable name="season" typeRef="string" />
注意,类型不是强制要求的,但是推荐提供。应该类型可以提供类型安全的表达式结果。
三、决策需求图(Decision Requirements Graph):
决策需求图(DRG)为决策领域建模,展示相关的最重要的元素和它们的依赖关系。建模的元素是决策(decisions),输入数据(input data),和知识源(knowledge sources)。
DRG的可视化表示称为决策需求图(DRD)。
在XML中,DRG由definitions元素表示。
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="dinnerDecisions" name="Dinner Decisions" namespace="http://camunda.org/schema/1.0/dmn">
<decision id="dish" name="Dish">
<!-- ... -->
</decision>
<decision id="beverages" name="Beverages">
<!-- ... -->
</decision>
</definitions>
1.1决策(Decision):
一个决策需求图可以有一个或者多个决策。决策有名字和编号。决策逻辑必须在决策表中或者决策表达式中。
决策在definitions元素中的decision元素表示:
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="dish" name="Desired Dish" namespace="party">
<decision id="beverages" name="Beverages">
<decisionTable id="decisionTable">
<!-- ... -->
</decisionTable>
</decision>
</definitions>
1.2被依赖的决策(Required Decisions):
一个决策可以有一个或者多个它依赖的决策,例如上图饮料的决策依赖食物的决策
一个必要的决策由informationRequirement元素内的requiredDecision元素表示。它有一个href属性和一个以#开始的值,这个值是必要的决策的编号(依赖的决策的id)。
<decision id="beverages" name="Beverages">
<informationRequirement>
<requiredDecision href="#dish" />
</informationRequirement>
<!-- ... -->
</decision>
1.3输入数据(Input Data):
输入数据表示一个或者多个决策的输入信息。
注意:输入数据没有执行的语义,并且会被camunda DMN引擎忽略。
它用definitions元素内的inputData元素表示:
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="dinnerDecisions" name="Dinner Decisions" namespace="http://camunda.org/schema/1.0/dmn">
<inputData id="guestsWithChildren" name="Guests with children?" />
<decision id="beverages" name="Beverages">
<informationRequirement>
<requiredInput href="#guestsWithChildren" />
</informationRequirement>
<!-- ... -->
</definitions>
1.4知识源(Knowledge Source):
知识源表示决策的权限。
注意:知识源没有执行的语义,并且会被camunda DMN引擎忽略。
它用definitions元素内的knowledgeSource元素表示:
<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="dinnerDecisions" name="Dinner Decisions" namespace="http://camunda.org/schema/1.0/dmn">
<knowledgeSource id="cookbook" name="Men's Cookbook" />
<decision id="dish" name="Dish">
<authorityRequirement>
<requiredDecision href="#cookbook" />
</authorityRequirement>
<!-- ... -->
</definitions>
三、足够友好的表达式语言(Friendly Enough Expression Language (FEEL)):
DMN提供了足够友好的表达式语言,它可以被用作执行决策表里的表达式
1.1 FEEL提供的数据类型(FEEL Data Types):
1.String:
FEEL支持字符串,字符串必须被双引号包裹起来。字符串只支持equal比较操作
2.Numeric Types:
FEEL支持数字类型,例如整数integer。在Camunda DMN引擎中可用的数字类型:
integer
long
double
数字类型支持所有的比较操作和范围操作
3.Boolean:
FEEL支持布尔值true和false。布尔值只支持equal比较操作
4.Date:
FEEL支持时间类型。Camunda DMN引擎中执行以下类型的时间:
date and time
要创建日期和时间值,必须将函数日期和时间与单个String参数一起使用。 该参数以yyyy-MM-dd'T'HH:mm:ss格式指定日期和时间。
Date支持所有的比较操作和范围
1.2 FEEL的语言元素(FEEL Language Elements):
Camunda DMN引擎支持FEEL输入条目。 输入条目中表达的FEEL术语是简单的一元测试。 这些简单的一元测试针对表达式测试输入值,如果满足测试,则返回true,否则返回false。 该表达式可以包含本节中描述的不同元素。
比较(Comparison):
FEEL的简单一元测试支持下面的比较操作。请注意,equals操作是空不是用=。另外,不等于操作不是!=,不等于操作要用逻辑非元素(negation)。
名称 | 操作 | 例子 | 描述 |
---|---|---|---|
等于 | "Steak" | 测试输入值是否等于给定的值 | |
小于 | < | <10 | 测试输入值是否小于给定的值 |
小于或等于 | <= | <=10 | 测试输入值是否小于等于给定的值 |
大于 | > | >10 | 测试输入值是否大于给定的值 |
大于或等于 | >= | >=10 | 测试输入值是否大于等于给定的值 |
范围(Range):
某些FEEL数据类型,例如数字和日期类型,可以测试是给定值是否属于某个范围。这个范围包括一个开始值和一个结束值。 还需指定开始和结束值是否包含在范围内。
开始 | 结束 | 例子 | 描述 |
---|---|---|---|
包含 | 包含 | [1..10] | 测试输入值是否大于等于开始值并且小于等于结束值 |
不包含 | 包含 | ]1..10] 或者 (1..10] | 测试输入值是否大于开始值并且小于等于结束值 |
包含 | 不包含 | [1..10[ 或者 [1..10) | 测试输入值是否大于等于开始值并且小于结束值 |
不包含 | 不包含 | ]1..10[ 或者 (1..10) | 测试输入值是否大于开始值并且小于结束值 |
析取(Disjunction):
FEEL简单一元测试可以指定一个联合表达式。这个联合表达式可以是比较和范围。当至少一个表达式是true,测试的结果就是true。
例子:
- 3,5,7: 测试输入值是否是3, 5或者7
- <2,>10: 测试输入值是否小于2或者大于10
- 10,[20..30]: 测试输入值是否是10,或者大于等于20且小于等于30
- "Spareribs","Steak","Stew": 测试输入值是否是Spareribs, Steak或者Stew
- date and time("2015-11-30T12:00:00"),date and time("2015-12-01T12:00:00"): 2015年11月30号12:00:00,或者2015年12月12:00:00
- >customer.age,>21: 测试输入值是否大于变量customer的age或者大于21
逻辑非(Negation):
FEEL简单的一元测试可以与not函数否定。 这意味着,如果包含的表达式返回true,则测试将返回false。 请注意,仅允许一个否定运算符作为第一运算符,它可以包含一个析取运算符。
例子:
- not("Steak"): 测试输入值是否不是Steak
- not(>10): 测试输入值是否不大于10,这意味着输入值是小于等于10的
- not(3,5,7): 测试输入值是否不是3, 5或者7
- not([20..30]): 测试输入值是否不是大于等于20且小于等于30
指定名称(Qualified Names):
FEEL简单一元表达式测试可以通过指定名称访问变量和对象的属性
例子:
- x: 测试输入值是否等于变量x的值
- >= x: 测试输入值是否大于等于变量x的值
- < customer.age: 测试输入值是否小于customer对象age属性的值
日期函数(Date Functions):
FEEL简单一元测试提供了函数去创建时间类型值。Camunda DMN引擎支持以下日期函数:
- date and time("..."): 用字符格式yyyy-MM-dd'T'HH:mm:ss值创建一个时期时间值
例子:
- date and time("2015-11-30T12:00:00"): 测试输入值是否是2015-11-30 12:00:00
- [date and time("2015-11-30T12:00:00")..date and time("2015-12-01T12:00:00")]: 测试输入值是否在2015-11-30 12:00:00和2015-12-01 12:00:00之间