Having some trouble with "dmn-scala/engine-rest" multi-level decisions


#1

I am trying to evaluate a top-level decision which has information requirements on lower level decisions using “dmn-scala/engine-rest” (https://github.com/camunda/dmn-scala/tree/master/engine-rest).

I have a DMN model with 3 decisions, of which the top-level one has an informationRequirement on the second and the second has an informationRequirement on the third:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="Definitions_17xik7z" name="DRD" namespace="http://camunda.org/schema/1.0/dmn">
  <decision id="Decision_13nychf" name="Level0">
    <extensionElements>
      <biodi:bounds x="604" y="194" width="180" height="80" />
      <biodi:edge source="Decision_1hbt6oo">
        <biodi:waypoints x="686" y="345" />
        <biodi:waypoints x="693" y="274" />
      </biodi:edge>
    </extensionElements>
    <informationRequirement>
      <requiredDecision href="#Decision_1hbt6oo" />
    </informationRequirement>
    <decisionTable id="decisionTable_1">
      <input id="input_1">
        <inputExpression id="inputExpression_1" typeRef="string">
          <text>outputLevel1</text>
        </inputExpression>
      </input>
      <output id="output_1" name="outputLevel0" typeRef="string" />
      <rule id="DecisionRule_13sy9z1">
        <inputEntry id="UnaryTests_1ml8kix">
          <text>"abc"</text>
        </inputEntry>
        <outputEntry id="LiteralExpression_01dx1nd">
          <text>"rule01-abc"</text>
        </outputEntry>
      </rule>
      <rule id="DecisionRule_10sym2q">
        <inputEntry id="UnaryTests_0cj8ojx">
          <text>"def"</text>
        </inputEntry>
        <outputEntry id="LiteralExpression_0adu62n">
          <text>"rule02-def"</text>
        </outputEntry>
      </rule>
      <rule id="DecisionRule_0hr1rk2">
        <inputEntry id="UnaryTests_0grq177">
          <text>"ghi"</text>
        </inputEntry>
        <outputEntry id="LiteralExpression_0euvidx">
          <text>"rule03-ghi"</text>
        </outputEntry>
      </rule>
      <rule id="DecisionRule_13uv3x7">
        <inputEntry id="UnaryTests_0se9eul">
          <text>"outputLevel1Value"</text>
        </inputEntry>
        <outputEntry id="LiteralExpression_05o9nx0">
          <text>"outputLevel0Value"</text>
        </outputEntry>
      </rule>
    </decisionTable>
  </decision>
  <decision id="Decision_0twsz81" name="level-2">
    <extensionElements>
      <biodi:bounds x="593" y="509" width="180" height="80" />
    </extensionElements>
    <decisionTable id="DecisionTable_0wt3cli">
      <input id="InputClause_1bnsdtf">
        <inputExpression id="LiteralExpression_0k4zt6g" typeRef="string">
          <text>inputLevel2</text>
        </inputExpression>
      </input>
      <output id="OutputClause_0u4xh5n" name="outputLevel2" typeRef="string" />
      <rule id="DecisionRule_0uadwhm">
        <inputEntry id="UnaryTests_10b4nsl">
          <text>"inputLevel2Value"</text>
        </inputEntry>
        <outputEntry id="LiteralExpression_00wa4ey">
          <text>"outputLevel2Value"</text>
        </outputEntry>
      </rule>
    </decisionTable>
  </decision>
  <decision id="Decision_1hbt6oo" name="level-1">
    <extensionElements>
      <biodi:bounds x="592" y="345" width="180" height="80" />
      <biodi:edge source="Decision_0twsz81">
        <biodi:waypoints x="680" y="509" />
        <biodi:waypoints x="673" y="425" />
      </biodi:edge>
    </extensionElements>
    <informationRequirement>
      <requiredDecision href="#Decision_0twsz81" />
    </informationRequirement>
    <decisionTable id="DecisionTable_11vv6vp">
      <input id="InputClause_13owqyl">
        <inputExpression id="LiteralExpression_17s5h1c" typeRef="string">
          <text>outputLevel2</text>
        </inputExpression>
      </input>
      <output id="OutputClause_1ee9l3u" name="outputLevel1" typeRef="string" />
      <rule id="DecisionRule_1y3153h">
        <inputEntry id="UnaryTests_1xpgt46">
          <text>"outputLevel2Value"</text>
        </inputEntry>
        <outputEntry id="LiteralExpression_1wa9z0j">
          <text>"outputLevel1Value"</text>
        </outputEntry>
      </rule>
    </decisionTable>
  </decision>
</definitions>

When trying to evaluate the top-level decision “Decision_13nychf” with

	{"inputLevel2": "inputLevel2Value"}

I get

{
    "message": "failed to evaluate expression 'outputLevel2':\nno variable found for name 'outputLevel2'"
}

When trying to evaluate the top-level decision “Decision_13nychf” with

	{"outputLevel2": "outputLevel2Value"}

I get

{
    "message": "failed to evaluate expression 'inputLevel2':\nno variable found for name 'inputLevel2'"
}

evaluating the bottom-level (third) decision “Decision_0twsz81” works fine:

	{"inputLevel2": "inputLevel2Value"}

results in

{
    "result": "outputLevel2Value"
}

#2

I was able to solve the issue by making the variable name in the failing input expression equal to the id of the required decision.

I initially thought this was a bug, as I was expecting the input expression to match the output name of the required decision, as is the case in e.g. the example of the “Dinner Decisions” model (https://camunda.com/dmn/simulator/), in which Beverages has “desiredDish” (output name) as input expression instead of “dish” (decision id).

However, when I consulted the DMN 1.1 specification, I read "A single output has no name, only a value. " on page 71.

Am I correct to conclude that it is the Camunda Modeler which has the bug and not the dmn-scala implementation?

Testing the dmn-scala further, I believe I came to the following conclusions.

  1. in case a required decision has a single output, what is passed through the requirement, is a single variable to be referenced using the decision id
  2. in case a required decision has multiple outputs, the result is passed on as an object with multiple attributes, which should be referenced individually as <decision_id>.

Could you please confirm, correct or elaborate on these, so I get a better understanding of the dmn-scala implementation vs the Camunda modeler?

Thanks in advance.


#3

Hi @bhoefman,

your conclusions are correct :+1:

It’s a bit different from the Camunda DMN engine (and also from the simulator). DMN-Scala has the goal to be more near to the specification.

BTW: congratulations. I guess that you are the first user of this extension :tada:

Best regards,
Philipp


#4

Thanks and great job! Keep up the good work :+1:


#5

Hi @Philipp_Ossler,

As you mention that the goal is to be closer to the specification, I was wondering if the dmn-scala supports the use of Business Knowledge Models.


#6

Yes, it does!

It supports decisions and business knowledge models with the following types:

  • decision table
  • decision literal expression
  • context
  • list
  • relation
  • function definition
  • invocation

#7

Nice! :+1:
Which modeler are you using to create models with these types of elements?


#8

Currently, I can’t recommend a modeler :sweat_smile:

I created the models manually or used the models from the DMN-TCK. The Camunda modeler only supports decision tables and decision literal expressions. You can have a look at this blog to get a first overview.


#9

Hi @Philipp_Ossler,

I tested some iteration patterns with BKM’s and it works like a charm. Great job :smiley::+1:
However, I would like to simplify my design a bit:

  • in my initial design, without BKMs / iterations, I have a number of levels of decisions, the lower levels of which are feeding into the higher ones up to a top-level decision
  • in the new design I wanted to iterate over that model, so I created a literal expression with “for each” logic calling a BKM which encapsulates the decision table in the original design. But I did this for each decision table - i.e. now I have a series of literal expressions leading up to a top level one each calling a different BKM - which seems a bit wasteful.

I was wondering if it is possible to apply the same pattern only once, calling the original model as a whole, i.e. the top level decision fed by the lower levels, rather than having to create a literal expression calling a BKM for each decision table.


#10

Hi @bhoefman,

do you call the same decision tables for each iteration?
If yes, you could bundle all invocation into a context and invoke the context in the loop.

Can you please share your example?


#11

Hi @Philipp_Ossler,

Indeed, I call the same tables for each iteration.

This is a dummy version of my original model without iteration: Some Decision Model.dmn (8.9 KB)

Would it also be possible to call the model in the attached file, i.e. without modifying the model as it is now, from a model in a separate file which takes care of the iteration?

On a separate note: “aCategory” and “aScore” were initially called “someCategory” and “someScore” respectively. This caused some errors because the engine interpreted the respective expressions as list expressions “some x in”…


#12

Hi @bhoefman,

Currently, it is not possible to import a file and use the decisions from it.

Can you also attach the version with iteration?


#13

Hi @Philipp_Ossler,

This is a dummy version of the iteration pattern: Some Decision Model Iteration.dmn (5.9 KB)

Please note that I dropped the third level decisions of the original model, i.e. it only contains 2 decisions each calling their corresponding BKM, which encapsulates the respective decision table of the 2 highest level decisions of the original model (instead of 4 decisions in the model without iteration).


#14

Hi @Philipp_Ossler,

Do you think that maybe a Decision Service, would be more apt to cater for the iteration requirement here?


#15

Hi @bhoefman,

I guess that you can’t get rid of the BKMs if you want to use the iteration. Also, I don’t think a Decision Service would help.

However, maybe you can simplify it a bit. One way is to combine the iteration expressions into one expression:
for aThing in things return aLevel(aScore(aThing))

Or, using a context instead of the literal expressions:

{ allScores: for aThing in things return aScore(aThing),
  for aScore in allScores return aLevel(aScore)
}

In the context, you invoke the decisions (with iteration) and calculate the final result in the last context entry (which has no name).

Does this help you?


#16

Hi @Philipp_Ossler,

Thanks, I will give it a try. :+1:

Note that I kept the 3rd level of the original model (“isSomething” and “aCategory” with as input “Something”) out for the purpose of simplicity. I’m afraid that with the suggested approach, readability decreases with each additional level, because it moves the graphical information requirements from the DRD to the context.

I was hoping that a decision service could solve this…
If a decision service or another construct cannot solve this issue, i.e. if it is not possible to invoke (part of) a DMN model visible on a DRD, then I assume it is more of a fundamental flaw in the 1.1 standard, which I hope will be solved in 1.2 or future versions.