springboot-drools-decision-table

Getting Started 🚀

Spring Boot 3 + Drools + Decision tables

springboot drools

Overview

Drools is an open-source business rule management system (BRMS) that helps businesses automate decision-making by defining rules that determine what actions to take in various situations. Drools is a java-based rules engine that allows you to separate business logic from the application code, making it more easier to maintain, update and reuse.

Drools Basics

There are several ways to define rules in Drools, making it flexible and accessible for different users from developers to business analysts. Here are the few primary methods:

  1. Drools Rule Language (DRL): A native language for writing rules in Drools, similar to Java syntax.

    Structure: Rules are written in plain text files with a .drl extension.

    Example:

     package package-name
     imports
     globals
     functions
     queries
     rule "rule name"
     // Attributes
     when
         LHS // Conditions
     then
         RHS // Actions
     end
     rule "rule2 name"
    
     ...
    
  2. Decision Tables: Rules can be defined in spreadsheets, typically Excel.

    Advantages: Easier for non-technical users to manage rules in a tabular format. Each row represents a rule with columns for conditions and actions. Example: discount.xls

  3. Domain-Specific Languages (DSLs): Allows creation of a custom language tailored to specific business domains.

    Structure: A DSL file with a .dsl extension is a text file in a line-oriented format. Its entries are used for transforming a DSLR file into a file according to DRL syntax. Example:

    [when]There is a customer with age greater than {age}=Customer(age > {age})
    [then]Approve the customer's loan=approveLoan($customer)
    

    In this example we have used Decision Tables by creating excel file spreadsheet inside the spring boot application.

Advantages of Using Decision Tables
  1. Clarity and Readability: Presents rules in a clear, tabular format that is easy to read and understand.
  2. Visualization: Provides a visual representation of rules making it easier to identify gaps or overlaps in logic.
  3. Ease of Maintenance: Simplifies the process of updating rules as changes can be made directly in the table without delving into complex code.
  4. Collaboration: Facilitates collaboration between business analysts and developers as both can work from the same table format.
  5. Efficiency: Reduces redundancy by consolidating multiple related rules into a single table.
  6. Consistency: Ensures consistent application of rules as the table format reduces the likelihood of errors and omissions.
When to Avoid Using Decision Tables
  1. Non-Tabular Logic: When the rules do not fit into a tabular structure or involve complex logic that cannot be easily expressed in a table format. Decision tables are not suitable for rules that require intricate conditional logic or multiple interdependencies that are difficult to represent in rows and columns.
  2. Small Number of Rules: When there are only a few rules to manage. The overhead of setting up and maintaining a decision table might not be justified for a small number of rules, which can be more easily managed directly in code or through simple rule definitions.
  3. Unstructured Rules: Decision tables work best for rules that have a uniform structure. If the rules are too diverse or unstructured, it can be challenging to fit them into a decision table format.
  4. Preference Against Spreadsheets: When there is a dislike or organizational policy against using spreadsheet software like Excel or OpenOffice.org.

Guides

In this example, we have defined the following rules using a decision table and the application will apply the highest possible discount on the purchase amount.

  1. If the total purchase amount is greater than or equal to 60,000 then a maximum highest discount rate 12% will be applied. image

  2. If the total purchase amount is greater than or equal to 40,000 then 6% discount(3rd highest discount rate) will be applied. image

  3. If the total purchase amount is greater than or equal to 30,000 and if the user is from Arunachal Pradesh, Jammu and Kashmir, Lakshadweep, or Assam then a state-specific discount (2nd highest discount rate ) will be applied. image

Q1: How can you stop rule execution once the first match is found in a decision table?

Approach: You can add an extra ACTION column and have in it “drools.halt();”. Then in the row, put some text (such as “Y” or TRUE or “stop”, the specific text doesn’t matter as long as there is something present) image

Technologies used in this example

Step 1: Create a spring boot application and add required dependencies

image

Step 2: Creating the Excel spreadsheet drools decision table

image

DRL format of the above drools decision table:

package rules;
//generated from Decision Table
import org.drools.example.model.RuleDataset;
import java.math.BigDecimal;
import java.math.RoundingMode;
function boolean hasTotalPurchaseAmountReachedThresholdAmount(BigDecimal totalPurchaseAmount, double thresholdAmount) {
       return totalPurchaseAmount.compareTo(BigDecimal.valueOf(thresholdAmount)) >=  0;
    }

 function BigDecimal getPayableAmount( BigDecimal totalPurchaseAmount, int discountRate) {
        return totalPurchaseAmount
                .subtract(totalPurchaseAmount
                        .multiply(BigDecimal.valueOf(discountRate))
                                .divide(BigDecimal.valueOf(100),2, RoundingMode.UP));
    }

// rule values at A13, header at A8
rule "Total purchase amount  >=  60000"
	salience 65535
	when
		ruleDataset: RuleDataset(hasTotalPurchaseAmountReachedThresholdAmount(ruleDataset.getTotalPurchaseAmount(), 60000))
	then
		ruleDataset.setDiscountAllowed(true) ;
		ruleDataset.setDiscountRate(12);
		ruleDataset.setPayableAmount(getPayableAmount(ruleDataset.getTotalPurchaseAmount(), ruleDataset.getDiscountRate()));
		drools.halt();
end

// rule values at A20, header at A15
rule "Total purchase amount  >=  30000 AND State code is JK"
	salience 65534
	when
		ruleDataset: RuleDataset(hasTotalPurchaseAmountReachedThresholdAmount(ruleDataset.getTotalPurchaseAmount(), 30000), ruleDataset.getStateCode() == "JK")
	then
		ruleDataset.setDiscountAllowed(true) ;
		ruleDataset.setDiscountRate(9);
		ruleDataset.setPayableAmount(getPayableAmount(ruleDataset.getTotalPurchaseAmount(), ruleDataset.getDiscountRate()));
		drools.halt();
end

// rule values at A21, header at A15
rule "Total purchase amount  >=  30000 AND State code LD"
	salience 65533
	when
		ruleDataset: RuleDataset(hasTotalPurchaseAmountReachedThresholdAmount(ruleDataset.getTotalPurchaseAmount(), 30000), ruleDataset.getStateCode() == "LD")
	then
		ruleDataset.setDiscountAllowed(true) ;
		ruleDataset.setDiscountRate(10);
		ruleDataset.setPayableAmount(getPayableAmount(ruleDataset.getTotalPurchaseAmount(), ruleDataset.getDiscountRate()));
		drools.halt();
end

// rule values at A22, header at A15
rule "Total purchase amount  >=  30000 AND State code is  AS"
	salience 65532
	when
		ruleDataset: RuleDataset(hasTotalPurchaseAmountReachedThresholdAmount(ruleDataset.getTotalPurchaseAmount(), 30000), ruleDataset.getStateCode() == "AS")
	then
		ruleDataset.setDiscountAllowed(true) ;
		ruleDataset.setDiscountRate(8);
		ruleDataset.setPayableAmount(getPayableAmount(ruleDataset.getTotalPurchaseAmount(), ruleDataset.getDiscountRate()));
		drools.halt();
end

// rule values at A23, header at A15
rule "Total purchase amount  >=  30000 AND State code are AR"
	salience 65531
	when
		ruleDataset: RuleDataset(hasTotalPurchaseAmountReachedThresholdAmount(ruleDataset.getTotalPurchaseAmount(), 30000), ruleDataset.getStateCode() == "AR")
	then
		ruleDataset.setDiscountAllowed(true) ;
		ruleDataset.setDiscountRate(10);
		ruleDataset.setPayableAmount(getPayableAmount(ruleDataset.getTotalPurchaseAmount(), ruleDataset.getDiscountRate()));
		drools.halt();
end

// rule values at A30, header at A25
rule "Total purchase amount  >=  40000"
	salience 65530
	when
		ruleDataset: RuleDataset(hasTotalPurchaseAmountReachedThresholdAmount(ruleDataset.getTotalPurchaseAmount(), 40000))
	then
		ruleDataset.setDiscountAllowed(true) ;
		ruleDataset.setDiscountRate(6);
		ruleDataset.setPayableAmount(getPayableAmount(ruleDataset.getTotalPurchaseAmount(), ruleDataset.getDiscountRate()));
		drools.halt();
end

Step 3: Add the drools configuration in spring boot application

image

We configured ‘StatelessKieSession’ as a Spring bean to be used in the service layer.

Step 4: Create a REST endpoint

image

Step 5: Add service layer

image

Step 6: Start the Spring Boot application and open the Swagger endpoint. image

http://localhost:8080/swagger-ui/index.html image

Input-1: image Output: image

Input-2: image Output: image

Input-3: image Output: image

Input-4: image Output: image

Reference Documentation

For further reference, please consider the following sections:

These additional references should also help you: