Decision Modeling with DMN

This section brings labs to help you get familiar with decision automation using the DMN standard and FEEL expression language.

The guides' instructions will give you a task to automate with specific goals. You should explore Kogito and Quarkus, develop, test and run the decision scenarios.

Some exercises were inspired by examples in the book DMN Cookbook and its free examples.

Use the power of Java in DMN

DMN supports business-level users to author most of the decision diagram and its core logic. One of this specification strengths is that it is not restricted to a single persona, instead, since it supports the cooperation also with technical teams. An example of a common practice is the usage of decoupled logic and reusable functions where the analyst delegates, when necessary, partial logic implementation to a developer, by for example, leveraging BKM to do an operation.

FEEL (Friendly enough expression language) brings several operations to support the logic implementation. On the other hand, we also have available the extensive options available in Java classes to support logic execution. In this lab, you will practice how to use the BKM node along with the power of Java classes and its methods to support DMN Decisions.

You’ll start by creating a basic DMN and some unit tests. The goal can be achieved achieved in different ways, but for the purpose of this exercise, you should create the actual logic by using a method in the Java String class.

Goals

Your decision should be able to receive a list of strings and return a concatenated single String separated by comma ",".The decision output should be a unique string containing every string from the list separated by a comma (e.g. ["John Doe","Ash"]) results in "John Doe, Ash").

Creating the project

The first step is to create a project to contain your decisions. Let’s use Kogito and Quarkus so that we can use the capabilities of automatic code generation.

  1. Create a new Quarkus application using the Quarkus archetype. Make sure to add:

    • Kogito Decision extension for decision execution capabilities;

    • Quarkus Smallrye Openapi to allow using Swagger to facilitate REST interaction via UI;

    • Kogito Scenario Simulation to allow using test scenarios (scesim) capabilities in your service;

      mvn io.quarkus.platform:quarkus-maven-plugin:2.8.2.Final:create \
          -DprojectGroupId=org.acme \
          -DprojectArtifactId=practicing-dmn-lab03 \
          -Dextensions="kogito-quarkus-decisions,quarkus-smallrye-openapi,quarkus-resteasy-jackson" \
          -DnoCode

Decision Diagram basic definition

  1. Create a new decision src/main/resources/DecisionWithJava.dmn

  2. In the Data Types of your decision, add a new List of strings with name tNameList.

    When adding the new type, select Type String and make sure to click on the list selector to categorize it as a list type.
  3. Back to the Editor, drag and drop an input node and name it Names;

  4. Configure the input Names with Data Type tNameList.

  5. Add a Decision Node named Final Name with Data Type string.

Now, before implementing the actual decision logic, let’s create a unit test to support us during the development process. We will use a test scenario to implement unit tests for this decision.

Start through the tests

  1. When developing a decision, one way to check if it is working is start your quarkus service in dev mode (mvn quarkus:dev) and invoke the respective REST endpoint passing an input and output until you’re satisfied. Once you’re happy with the result, you can move ahead and start writing the unit test.

    Another approach, is to start with the unit testing and validate as many valid and invalid inputs and resulting execution outputs. Once the test is finished, you code your decision, checking its validity through automated test. With this approach, once you can successfully run your tests, you’ll have completed both implementation and its respective unit test.

    To know more about Test Driven Development practices, check the TDD Manifesto.
  2. Open the pom.xml and add the dependency to the test scenario library so we can use Test Scenario capabilities.

    <dependency>
      <groupId>org.kie.kogito</groupId>
      <artifactId>kogito-scenario-simulation</artifactId>
      <scope>test</scope>
    </dependency>
  3. Add two new test directory structures: src/test/resources and src/test/java/testscenario.

  4. Create a new Java class that will allow our test scenarios to be part of the maven test lifecycle within JUnit scope. In src/main/java/testscenario add the new Java class KogitoScenarioJunitActivatorTest.java with the following content:

    package testscenario;
    
    /**
     * KogitoJunitActivator is a custom JUnit runner that enables the execution of Test Scenario files (*.scesim).
     * This activator class, when executed, will load all scesim files available in the project and run them.
     * Each row of the scenario will generate a test JUnit result.
     */
    @org.junit.runner.RunWith(org.kogito.scenariosimulation.runner.KogitoJunitActivator.class)
    public class KogitoScenarioJunitActivatorTest {
    }
  5. Now, in src/test/resources/ add a new Test Scenario called DecisionWithJavaTest.scesim.

  6. decision needs to will receive an Array of Strings and returns a String resulted from the concatenation of all the elements.

  7. Once you open your new test scenario file (if the Red Hat Business Automation Bundle VSCode extension is installed), you should be able to choose DMN, and select your DMN file in the list of available decisions.

  8. Click create once you finish. The test scenario should be already configured using the Input and Outputs of the Decision file you selected.

  9. Now, add the following validation scenarios:

    • Should return an empty String for a list with zero elements. (e.g. "")

    • Should return a String for a list with one element (string). (e.g. "Karina")

    • Should return a String with words separated by comma for a list with two elements (e.g. "Karina,Varela")

Check below some tips on the configuration of the list of given inputs and the final test implementation:

  • Configured list input with "Create List" guided form.

You can also use the option "Define List" with value like ["John Doe","Ash"].
Test input configuration
  • Test Scenario example:

decision with java test scenario

If you open your project in a command line and execute the tests using maven, you should be able to see your broken tests. Something similar to:

$ mvn test
(...)

[INFO]
[INFO] Results:
[INFO]
[ERROR] Errors:
[ERROR]   #1 Should return an empty string for empty list: Failure reason: The decision Final Name has not been successfully evaluated: SKIPPED (DecisionWithJavaTest)
[ERROR]   #2 Should return a string for one element list: Failure reason: The decision Final Name has not been successfully evaluated: SKIPPED (DecisionWithJavaTest)
[ERROR]   #3 Should return string with the concatenated elements separated by comma: Failure reason: The decision Final Name has not been successfully evaluated: SKIPPED (DecisionWithJavaTest)
[INFO]
[ERROR] Tests run: 3, Failures: 0, Errors: 3, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------

Implementing the Decision Logic

With the project, a basic DMN and some unit tests, we now have a good base structure to start coding our decision logic. The same result could be achieved in many different ways, but for the purpose of this exercise, you should create the actual logic by using the Java String class.

  • Goals: Fix the DecisionWithJava.dmn to concatenate the incoming list of strings and return a single String. The decision output should be a unique string containing every string from the list separated by a space (e.g. ["John","Doe","Ash"]) results in "John Doe Ash").

  • Try to accomplish these in your solution:

    • The decision node Final Name should rely on a BKM Node: this will decouple and externalize the technical implementation aspects of the decision logic.

    • When invoking the solution provided by the BKM, the decision node Final Name should send the list Names and ",", a space character to be used as the delimiter when concatenating the list of strings.

    • Use the class java.lang.String. It has a static method "join" that may be useful. This method expects, in simple terms, two parameters: the delimiter used in between concatenated elements, and a list.

Before checking the following hints, try to implement this and adjust your decision until you get all the tests to pass. If you need, check the hints below.

Solution Tips

  • Use a Business Knowledge Model (BKM) node to create the reusable logic that will be invoked by the Final Name decision node.

    • It should be a Function, and the function kind should be Java (J).

    • Add two parameters, one named delimiter( with type string) and one names list (with type tNamesList). These will be sent by the node invoking this BKM, and will be sent as input to the Java method.

    • The class can be configured as java.lang.String;

    • The method signature can be configured with "join(java.lang.CharSequence,java.lang.Iterable)".

  • The decision node Final Name can be implemented with a boxed expression of type Invocation.

    • On the second table cell (with default value Enter function) type the name of your BKM.

    • Add two lines, one with parameter name delimiter and literal value "," ; The other with name list and literal value Names. Here, you are configuring the values you want to send to the BKM, where you are referencing the input Names as the list.

Party Challenge

In this challenge, you’re requested to create a decision service that helps a party owner to decide whether or not a guest can join the party.

During this challenge development you will exercise FEEL, String manipulation, list manipulation, temporal operators, Data Types, different decision options and more.
  • Goal: Return true or false, depending on the fact that a Guest can join a party on a specific Planned Date.

  • Party rules:

    • To make a decision on whether a specific Guest Can Party, the party owner will inform:

      • A Guest, with name (text) and birthdate ("YYYY-MM-DD" format).

      • A Planned Date for this party ("YYYY-MM-DD" format).

    • Only guests that are 18 years or more can party.

    • There is a Banned guests list, that defines that "Chucky" and "Carrie" can’t party.

Input Example:

Example of input that can be sent to validate Karina V, born on Jan 1st 1980, can join a party that will happen on 25th Oct 2021:

{
    "Guest": {
        "birthdate": "1980-01-01",
        "name": "Karina V"
    },
    "Planned date": "2021-10-25"
}

Automate the validation with unit tests

The user party wants to be able to read and increment the automated tests once your solution is delivered to him. Initially you need to implement automated tests to validate that:

  • Guest "Chucky" can’t party.

  • Guest "Karina" can party.

  • Guest under 18y can’t party.

  • Guests over 18y can party.

  • Guest that is not yet 18, but will be on the party planned date, can party.