DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Data Engineering
  3. Databases
  4. Decision Table Modeling and Evaluation in Power Flows DMN

Decision Table Modeling and Evaluation in Power Flows DMN

Let's look at decision table modeling and evaluation in Power Flows DMN.

Mariusz Kumor user avatar by
Mariusz Kumor
·
May. 20, 19 · Analysis
Like (2)
Save
Tweet
Share
3.74K Views

Join the DZone community and get the full member experience.

Join For Free

Power Flows, being a full-fledged and versatile decision engine, allows modeling decision tables and their evaluation. The source code is available on GitHub, and the home page can be found at Power Flows. The decision engine itself was written in Java. The latest version, 2.0.0, supports the modularity of dependencies, and this article will be based on it.

Decisions Modeling

For modeling, you can use several approaches supported by the DMN Power Flows. This is, among others:

  • YAML file — on YAML syntax basis. Very concise and precise format, which I personally recommend

  • Java or Groovy file — here there are two approaches. Fluent style and functional style. Both work great in specific cases, especially when you prefer the modeling in the languages in which you feel best and can be either Java or very close to Groovy. This approach gives us automatically highlighting a syntax, possible and available fields, etc. if you use one of the leading IDEs, for example, Eclipse, NetBeans, IntelliJ IDEA. This modeling approach is extremely easy.

  • DSL in Kotlin — this is a novelty that came with version 2.0.0. Thanks to DSL, it is possible to model using a concise syntax, and at the same time, gets a suggestion of available properties. No advanced Kotlin knowledge and experience is required here.

  • XML file — supported for the purpose of compliance with the original OMG DMN definition defined on XML basis, but due to the fact of wordy form of the notation, it is not personally recommended, it is useful when you have an existing project that uses a different XML-only decision engine, and you would like to quickly make your decision tables run on Power Flows DMN before you start modeling in the recommended formats. Unfortunately, some elements from the XML of other decision engine vendors may not be supported.

For the purposes of this article, I will use sample models from the project repository. You can decide on one of the below examples and go further to an evaluation step. 

YAML File Modeling Approach

id: loan_qualifier
name: Loan qualifier
hit-policy: COLLECT
expression-type: FEEL
fields:
  in:
    age:
      type: INTEGER
    activeLoansNumber:
      description: Number of active loans on user's account
      type: INTEGER
      expression-type: LITERAL
    startDate:
      type: DATE
  out:
    loanAmount:
      description: Loan amount in Euro
      type: DOUBLE
    loanTerm:
      description: Loan term in months
      type: INTEGER
rules:
- description: Loan for 18 years
  in:
    age: 18
    activeLoansNumber: 0
    startDate: '[date and time("2019-01-01T12:00:00")..date and time("2019-12-31T12:00:00")]'
  out:
    loanAmount: 10000
    loanTerm: 12
- in:
    age: 18
    startDate: '[date and time("2019-03-01T12:00:00")..date and time("2019-03-31T12:00:00")]'
  out:
    loanAmount: 15000
    loanTerm: 6
- description: Loan for older than 18 years
  in:
    age: '>18'
  out:
    loanAmount: 20000
    loanTerm: 12

Java/Groovy Fluet Style Modeling Approach

Decision decision = Decision.fluentBuilder()
.id("loan_qualifier")
.name("Loan qualifier")
.hitPolicy(HitPolicy.COLLECT)
.expressionType(ExpressionType.FEEL)
.withInputs()
    .name("age")
    .type(ValueType.INTEGER)
    .next()
    .name("activeLoansNumber")
    .description("Number of active loans on user's account")
    .type(ValueType.INTEGER)
    .withExpression()
        .type(ExpressionType.LITERAL)
        .and()
    .next()
    .name("startDate")
    .type(ValueType.DATE)
.end()
.withOutputs()
    .name("loanAmount")
    .description("Loan amount in Euro")
    .type(ValueType.DOUBLE)
    .next()
    .name("loanTerm")
    .description("Loan term in months")
    .type(ValueType.INTEGER)
.end()
.withRules()
    .description("Loan for 18 years")
    .withInputEntries()
        .name("age")
        .withExpression()
            .type(ExpressionType.FEEL)
            .value(18)
            .and()
        .next()
        .name("activeLoansNumber")
        .evaluationMode(EvaluationMode.INPUT_COMPARISON)
        .withExpression()
            .type(ExpressionType.LITERAL)
            .value(0)
            .and()
        .next()
        .name("startDate")
        .withExpression()
            .type(ExpressionType.FEEL)
            .value("[date and time(\"2019-01-01T12:00:00\")..date and time(\"2019-12-31T12:00:00\")]")
            .and()
    .end()
    .withOutputEntries()
        .name("loanAmount")
        .withExpression()
            .type(ExpressionType.LITERAL)
            .value(10000)
            .and()
        .next()
        .name("loanTerm")
        .withExpression()
            .type(ExpressionType.LITERAL)
            .value(12)
            .and()
    .end()
    .next()
    .withInputEntries()
        .name("age")
        .evaluationMode(EvaluationMode.INPUT_COMPARISON)
        .withExpression()
            .type(ExpressionType.LITERAL)
            .value(18)
            .and()
        .next()
        .name("startDate")
        .withExpression()
            .type(ExpressionType.FEEL)
            .value("[date and time(\"2019-03-01T12:00:00\")..date and time(\"2019-03-31T12:00:00\")]")
            .and()
    .end()
    .withOutputEntries()
        .name("loanAmount")
        .withExpression()
            .type(ExpressionType.LITERAL)
            .value(15000)
.           and()
        .next()
        .name("loanTerm")
        .withExpression()
            .type(ExpressionType.LITERAL)
            .value(6)
            .and()
    .end()
    .next()
    .withInputEntries()
        .name("age")
        .withExpression()
            .type(ExpressionType.FEEL)
            .value(">18")
            .and()
    .end()
    .withOutputEntries()
        .name("loanAmount")
        .withExpression()
            .type(ExpressionType.LITERAL)
            .value(20000)
            .and()
        .next()
        .name("loanTerm")
        .withExpression()
            .type(ExpressionType.LITERAL)
            .value(12)
            .and()
    .end()
.end()
.build();

Power Flows DMN has prepared a set of comprehensive builders. The built code looks very clear and readable. Of course, it has many advantages. However, the disadvantage is the need for manual formatting. IDE environments are not yet able to format such extended fluent syntax for builders. In order to protect your manually formatted code, i.e. in IntelliJ IDEA, you can use respectively before a start of modeling:

// @formatter:off

and after:

// @formatter:on

Then IntelliJ IDEA will not format your code.

Java/Groovy Functional Style Modeling Approach

Decision decision = Decision.builder()
.id("loan_qualifier")
.name("Loan qualifier")
.hitPolicy(HitPolicy.COLLECT)
.expressionType(ExpressionType.FEEL)
.withInput(in -> in
        .name("age")
        .type(ValueType.INTEGER)
        .build())
.withInput(in -> in
        .name("activeLoansNumber")
        .description("Number of active loans on user's account")
        .type(ValueType.INTEGER)
        .withExpression(ex -> ex
                .type(ExpressionType.LITERAL)
                .build())
        .build())
.withInput(in -> in
        .name("startDate")
        .type(ValueType.DATE)
        .build())
.withOutput(out -> out
        .name("loanAmount")
        .description("Loan amount in Euro")
        .type(ValueType.DOUBLE)
        .build())
.withOutput(out -> out
        .name("loanTerm")
        .description("Loan term in months")
        .type(ValueType.INTEGER)
        .build())
.withRule(rule -> rule
        .description("Loan for 18 years")
        .withInputEntry(in -> in
                .name("age")
                .evaluationMode(EvaluationMode.INPUT_COMPARISON)
                .withExpression(ex -> ex
                        .type(ExpressionType.LITERAL)
                        .value(18)
                        .build())
                .build())
        .withInputEntry(in -> in
                .name("activeLoansNumber")
                .evaluationMode(EvaluationMode.INPUT_COMPARISON)
                .withExpression(ex -> ex
                        .type(ExpressionType.LITERAL)
                        .value(0)
                        .build())
                .build())
        .withInputEntry(in -> in
                .name("startDate")
                .withExpression(ex -> ex
                        .type(ExpressionType.FEEL)
                        .value("[date and time(\"2019-01-01T12:00:00\")..date and time(\"2019-12-31T12:00:00\")]")
                        .build())
                .build())
        .withOutputEntry(out -> out
                .name("loanAmount")
                .withExpression(ex -> ex
                        .type(ExpressionType.LITERAL)
                        .value(10000)
                        .build())
                .build())
        .withOutputEntry(out -> out
                .name("loanTerm")
                .withExpression(ex -> ex
                        .type(ExpressionType.LITERAL)
                        .value(12)
                        .build())
                .build())
        .build())
.withRule(rule -> rule
        .withInputEntry(in -> in
                .name("age")
                .withExpression(ex -> ex
                        .type(ExpressionType.FEEL)
                        .value(18)
                        .build())
                .build())
        .withInputEntry(in -> in
                .name("startDate")
                .withExpression(ex -> ex
                        .type(ExpressionType.FEEL)
                        .value("[date and time(\"2019-03-01T12:00:00\")..date and time(\"2019-03-31T12:00:00\")]")
                        .build())
                .build())
        .withOutputEntry(out -> out
                .name("loanAmount")
                .withExpression(ex -> ex
                        .type(ExpressionType.LITERAL)
                        .value(15000)
                        .build())
                .build())
        .withOutputEntry(out -> out
                .name("loanTerm")
                .withExpression(ex -> ex
                        .type(ExpressionType.LITERAL)
                        .value(6)
                        .build())
                .build())
        .build())
.withRule(rule -> rule
        .withInputEntry(in -> in
                .name("age")
                .withExpression(ex -> ex
                        .type(ExpressionType.FEEL)
                        .value(">18")
                        .build())
                .build())
        .withOutputEntry(out -> out
                .name("loanAmount")
                .withExpression(ex -> ex
                        .type(ExpressionType.LITERAL)
                        .value(20000)
                        .build())
                .build())
        .withOutputEntry(out -> out
                .name("loanTerm")
                .withExpression(ex -> ex
                        .type(ExpressionType.LITERAL)
                        .value(12)
                        .build())
                .build())
        .build())
.build();

Kotlin DSL Style Modeling Approach

val decision = decision {
    id = "loan_qualifier"
    name = "Loan qualifier"
    hitPolicy = HitPolicy.COLLECT
    expressionType = ExpressionType.FEEL
    inputs {
        input("age") {
            type = ValueType.INTEGER
        }
        input("activeLoansNumber") {
            description = "Number of active loans on user's account"
            type = ValueType.INTEGER
            expression(ExpressionType.LITERAL)
        }
        input("startDate") {
            type = ValueType.DATE
        }
    }
    outputs {
        output("loanAmount") {
            description = "Loan amount in Euro"
            type = ValueType.DOUBLE
        }
        output("loanTerm") {
            description = "Loan term in months"
            type = ValueType.INTEGER
        }
    }
    rules {
        rule {
            input("age") {
                value = 18
            }
            input("activeLoansNumber")
            input("startDate") {
                expression("[date and time(\"2019-01-01T12:00:00\")..date and time(\"2019-12-31T12:00:00\")]")
            }
            output("loanAmount") {
                value = 10000
            }
            output("loanTerm") {
                value = 12
            }
        }
        rule {
            input("age") {
                value = 18
            }
            input("startDate") {
                expression("[date and time(\"2019-03-01T12:00:00\")..date and time(\"2019-03-31T12:00:00\")]")
            }
            output("loanAmount") {
                value = 15000
            }
            output("loanTerm") {
                value = 6
            }
        }
        rule {
            description = "Loan for older than 18 years"
            input("age") {
                expression(">18")
            }
            output("loanAmount") {
                value = 20000
            }
            output("loanTerm") {
                value = 12
            }
        }
    }
}

Reading YAML File to Decision Model

In case you decided to go with YAML, having the decision model in *.yaml file, Power Flows DMN allows to read it and convert to Decision class as follows:

File loanQualifierFile = new File("loan-qualifier.yml");
InputStream loanQualifierInputStream = new FileInputStream(loanQualifierFile);

Decision decision = new YamlDecisionReader().read(loanQualifierInputStream).get();

Decision Evaluation

The next step is the evaluation process of the prepared model as Decision class object. To do this, you can use the appropriate API provided with the decision engine:

DecisionEngine decisionEngine = new DefaultDecisionEngineConfiguration().configure();

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

Map<String, Serializable> variables = new HashMap<>();
variables.put("age", 18);
variables.put("activeLoansNumber", 0);
variables.put("startDate", format.parse("2019-01-05"));
DecisionVariables decisionVariables = new DecisionVariables(variables);

DecisionResult decisionResult = decisionEngine.evaluate(decision, decisionVariables);

At the end having the evaluation decisionResult, you can operate on it using the provided methods:

decisionResult.isSingleEntryResult();
decisionResult.isSingleRuleResult();
decisionResult.isCollectionRulesResult();
decisionResult.getSingleEntryResult();
decisionResult.getSingleRuleResult();
decisionResult.getCollectionRulesResult();

Firstly, it is a good practice to check how many entries the result contains. For this, you can call i.e. isCollectionRulesResult() method.

And if true, get the whole collection using getCollectionRulesResult() method. And now you can process the result according to your need. DecisionResult contains a list of RuleResult and each RuleResult has EntryResult with name and value.

Flow (web browser) Evaluation Database intellij

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • [DZone Survey] Share Your Expertise and Take our 2023 Web, Mobile, and Low-Code Apps Survey
  • How Elasticsearch Works
  • Monolithic First
  • Practical Example of Using CSS Layer

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: