Drools rule engine introduction demo

1. Problem elicitation

There is a business scenario of online credit card application. Users need to enter personal information, as shown in the figure below:

As can be seen from the above figure, the personal information entered by the user includes name, gender, age, education background, telephone, company, position, monthly income, whether there is a house, car, credit card, etc. After entry, click the application button to submit.

After submitting the application, users need to check the legitimacy of user information at the server of the system (whether they are qualified to apply for a credit card). Only users who pass the legitimacy check can successfully apply for a credit card (Note: different users may apply for different credit card limits).

The rules for checking the legitimacy of user information are as follows:

Rule numbernamedescribe
1Check education and salary 1If the applicant has neither a house nor a car, has a college degree or below, and has a monthly salary of less than 5000, he will not pass the examination
2Check education and salary 2If the applicant has neither a house nor a car, has a college degree or bachelor's degree, and the monthly salary is less than 3000, he will not pass the examination
3Check education and salary 3If the applicant has neither a house nor a car, has a bachelor's degree or above, has a monthly salary of less than 2000, and has no credit card before, it will not pass
4Check the number of credit cards the applicant hasIf the applicant's existing number of credit cards is greater than 10, it will not pass

After the legitimacy check of user information is passed, the credit card limit of the user shall also be determined according to the following credit card issuance rules:

Rule numbernamedescribe
1Rule 1If the applicant has a house and a car, or the monthly income is more than 20000, the credit card limit issued is 15000
2Rule 2If the applicant has no room or car, but the monthly income is between 10000 and 20000, the credit card limit issued is 6000
3Rule 3If the applicant has no house or car and the monthly income is less than 10000, the credit card limit issued is 3000
4Rule 4If the applicant has a house but no car or has no house but a car, and the monthly income is less than 10000, the credit card limit issued is 5000
5Rule 5If the applicant has a house but no car or has no house but a car, and the monthly income is between 10000 and 20000, the credit card limit issued is 8000

Thinking: how to implement the above business logic?

The easiest thing we can think of is to use branch judgment (if else), for example, to check the legitimacy of user information through the following code:

//Pseudo code here

//Check the validity of user information. Returning true means that the check passes, and returning false means that the check fails
public boolean checkUser(User user){
    //If the applicant has neither a house nor a car, has a college degree or below, and has a monthly salary of less than 5000, he will not pass the examination
    if(user.getHouse() == null 
       && user.getcar() == null 
       && user.getEducation().equals("Below junior college") 
       && user.getSalary < 5000){
        return false;
    }
    //If the applicant has neither a house nor a car, has a college degree or bachelor's degree, and the monthly salary is less than 3000, he will not pass the examination
    else if(user.getHouse() == null 
       && user.getcar() == null 
       && user.getEducation().equals("Diploma or undergraduate ") 
       && user.getSalary < 3000){
        return false;
    }
    //If the applicant has neither a house nor a car, has a bachelor's degree or above, has a monthly salary of less than 2000, and has no credit card before, it will not pass
    else if(user.getHouse() == null 
       && user.getcar() == null 
       && user.getEducation().equals("Bachelor degree or above") 
       && user.getSalary < 2000 
       && user.getHasCreditCard() == false){
        return false;
    }
    //If the applicant's existing number of credit cards is greater than 10, it will not pass
    else if(user.getCreditCardCount() > 10){
        return false;
    }
    return true;
}

If the validity check of the user's information is passed, the credit card limit of the user needs to be determined through the following code:

//Pseudo code here

//Determine the credit card limit according to the user input information
public Integer determineCreditCardLimit(User user){
    //If the applicant has a house and a car, or the monthly income is more than 20000, the credit card limit issued is 15000
    if((user.getHouse() != null && user.getcar() != null) 
       || user.getSalary() > 20000){
        return 15000;
    }
    //If the applicant has no room or car, and the monthly income is between 10000 and 20000, the credit card limit issued is 6000
    else if(user.getHouse() == null 
       && user.getcar() == null
       && user.getSalary() > 10000 
       && user.getSalary() < 20000){
        return 6000;
    }
    //If the applicant has no room or car, and the monthly income is less than 10000, the credit card limit issued is 3000
    else if(user.getHouse() == null 
       && user.getcar() == null
       && user.getSalary() < 10000){
        return 3000;
    }
    //If the applicant has a room but no car or no room but has a car, and the monthly income is less than 10000, the credit card limit issued is 5000
    else if((((user.getHouse() != null && user.getcar() == null) || (user.getHouse() == null && user.getcar() != null))
       && user.getSalary() < 10000){
        return 5000;
    }
    //If the applicant has a room but no car or has no room but a car, and the monthly income is between 10000 and 20000, the credit card limit issued is 8000
    else if((((user.getHouse() != null && user.getcar() == null) || (user.getHouse() == null && user.getcar() != null))
       && (user.getSalary() > 10000 && user.getSalary() < 20000)){
        return 8000;
    }
}

From the pseudo code above, we can see that our business rules are implemented through Java code. This implementation has the following problems:

1. Hard coded business rules are difficult to maintain

2. Hard coded business rules are difficult to cope with changes

3. Changes to business rules require code modification and take effect only after the service is restarted

So, what are the good implementation methods for the above business scenarios?

The answer is the rule engine.

2. Rule engine overview

2.1 what is a rule engine

The full name of rule engine is business rule management system, and its English name is BRMS (Business Rule Management System). The main idea of rule engine is to separate the business decision-making part of the application, and write business decisions (business rules) using predefined semantic modules, which can be configured and managed by users or developers when needed.

It should be noted that the rule engine is not a specific technical framework, but a kind of system, that is, business rule management system. At present, specific rule engine products on the market include drools, VisualRules, iLog, etc.

The rule engine separates business decisions from application code, receives data input, interprets business rules, and makes business decisions according to business rules. The rule engine is actually an input and output platform.

After using the rule engine in the above credit card application business scenario, the effect is as follows:

After the rule engine is introduced into the system, the business rules no longer reside in the system in the form of program code, but are replaced by the rule engine for processing rules. The business rules are stored in the rule base and completely independent of the program. Business personnel can manage business rules like managing data, such as querying, adding, updating, statistics, submitting business rules, etc. Business rules are loaded into the rule engine to supply system calls.

2.2 advantages of using rule engine

The advantages of using rule engine are as follows:

1. Business rules are separated from system code to realize centralized management of business rules

2. Business rules can be extended and maintained at any time without restarting the service

3. Business rules can be dynamically modified to quickly respond to changes in requirements

4. The rule engine is relatively independent and only cares about business rules, so that business analysts can also participate in editing and maintaining system business rules

5. Reduces the cost and risk of hard coding business rules

6. Using the rule editing tool provided by the rule engine makes the implementation of complex business rules easier

2.3 application scenario of rule engine

For some systems with complex business rules and frequent changes in business rules, it is more suitable to use the rule engine, as follows:

1. Risk control system - risk loan, risk assessment

2. Anti fraud project - bank loan, credit investigation and verification

3. Decision making platform system - financial calculation

4. Promotion platform system - full reduction, discount and price increase purchase

2.4 introduction to drools

drools is an open source rule engine developed based on Java language provided by JBoss organization. It can liberate complex and changeable business rules from hard coding and store them in files or specific storage media (such as database) in the form of rule scripts, so that the change of business rules does not need to modify the project code Restart the server and the online environment will take effect immediately.

drools official website address: https://drools.org/

drools source code download address: https://github.com/kiegroup/drools

When using drools in a project, it can be used alone or integrated with spring. If used alone, you only need to import the following maven coordinates:

<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>7.6.0.Final</version>
</dependency>

If we use IDEA to develop drools applications, the drools plug-in has been integrated into the IDEA. If you use eclipse to develop drools applications, you also need to install the drools plug-in separately.

The development steps of drools API are as follows:

3. Introduction to drools

In this section, an introductory case of Drools is used to give you a preliminary understanding of how Drools is used and an overall concept of Drools.

3.1 business scenario description

Business scenario: when consumers buy books in the book mall, they need to display the price after preferential order on the payment page after placing an order. The specific preferential rules are as follows:

Rule numberRule namedescribe
1Rule oneThere is no discount for books with a total price of less than 100 yuan
2Rule 2If the total price of the books purchased is 100 to 200 yuan, the discount is 20 yuan
3Rule threeIf the total price of the books purchased is 200 to 300 yuan, the discount is 50 yuan
4Rule 4If the total price of the books purchased is more than 300 yuan, the discount is 100 yuan

Now you need to calculate the preferential price according to the above rules.

3.2 development and Realization

Step 1: create maven project drools_quickstart and import maven coordinates related to drools

<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>7.10.0.Final</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

Step 2: create the resources / meta-inf / kmmodule.xml configuration file according to the requirements of drools

<?xml version="1.0" encoding="UTF-8" ?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <!--
        name:appoint kbase The name of can be arbitrary, but it needs to be unique
        packages:Specify the directory of the rule file, which needs to be filled in according to the actual situation, otherwise it cannot be loaded into the rule file
        default:Specify current kbase Default
    -->
    <kbase name="myKbase1" packages="rules" default="true">
        <!--
            name:appoint ksession The name can be arbitrary, but it needs to be unique
            default:Specify current session Default
        -->
        <ksession name="ksession-rule" default="true"/>
    </kbase>
</kmodule>

Note: the name and location of the above configuration file are fixed and cannot be changed

Step 3: create the entity class Order

package com.example.demo.entity;

/**
 * order
 * @author admin
 */
public class Order {
    /**
     * The original price of the order, i.e. the price before the discount
     */
    private Double originalPrice;

    /**
     * The real price of the order, that is, the price after discount
     */
    private Double realPrice;

    @Override
    public String toString() {
        return "Order{" +
                "originalPrice=" + originalPrice +
                ", realPrice=" + realPrice +
                '}';
    }

    public Double getOriginalPrice() {
        return originalPrice;
    }

    public void setOriginalPrice(Double originalPrice) {
        this.originalPrice = originalPrice;
    }

    public Double getRealPrice() {
        return realPrice;
    }

    public void setRealPrice(Double realPrice) {
        this.realPrice = realPrice;
    }
}

Step 4: create the rule file resources / rules / bookdiscount.drl (the DRL file name can be arbitrary)

//Book discount rules
package book.discount
import com.example.demo.entity.Order

//Rule 1: there is no discount for books with a total price of less than 100 yuan
rule "book_discount_1"
    when
        $order:Order(originalPrice < 100)
    then
        $order.setRealPrice($order.getOriginalPrice());
        System.out.println("Successfully matched to rule 1: there is no discount if the total price of the purchased books is less than 100 yuan");
end

//Rule 2: if the total price of the purchased books is 100 to 200 yuan, the discount is 20 yuan
rule "book_discount_2"
    when
        $order:Order(originalPrice < 200 && originalPrice >= 100)
    then
        $order.setRealPrice($order.getOriginalPrice() - 20);
        System.out.println("Successfully matched to rule 2: 20 yuan discount for books with a total price of 100 to 200 yuan");
end

//Rule 3: if the total price of the purchased books is 200 to 300 yuan, the discount is 50 yuan
rule "book_discount_3"
    when
        $order:Order(originalPrice <= 300 && originalPrice >= 200)
    then
        $order.setRealPrice($order.getOriginalPrice() - 50);
        System.out.println("Successfully matched to rule 3: 50 yuan discount for books with a total price of 200 to 300 yuan");
end

//Rule 4: if the total price of the books purchased is more than 300 yuan, the discount is 100 yuan
rule "book_discount_4"
    when
        $order:Order(originalPrice >= 300)
    then
        $order.setRealPrice($order.getOriginalPrice() - 100);
        System.out.println("Successfully matched to rule 4: if the total price of the purchased books is more than 300 yuan, the discount is 100 yuan");
end

Step 5: write unit tests

package com.example.demo.test;



import com.example.demo.entity.Order;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

public class Test {

	@org.junit.Test
	public void test1(){
		KieServices kieServices = KieServices.Factory.get();
		KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
		//Session object for interacting with the rule engine
		KieSession kieSession = kieClasspathContainer.newKieSession("ksession-rule");

		//Construct the order object, set the original price, and the rule engine calculates the preferential price according to the preferential rules
		Order order = new Order();
		order.setOriginalPrice(210D);

		//Provide the data to the rule engine, and the rule engine will match the rules according to the provided data
		kieSession.insert(order);

		//Activate the rule engine. If the rules match successfully, execute the rules
		kieSession.fireAllRules();
		//Close session
		kieSession.dispose();

		System.out.println("Original price before offer:" + order.getOriginalPrice() +
						"´╝îPrice after discount:" + order.getRealPrice());
	}

}

Through the above introductory case, we can find that the main work of using drools rule engine is to write rule files and define business-related business rules in the rule files. For example, this case defines book discount rules. After the rules are defined, you need to call the API provided by drools to provide the data to the rule engine for rule pattern matching. The rule engine will execute the successfully matched rules and return the calculation results to us.

You may have a question, that is, although we do not write the judgment logic of rules in the code, we still write business rules in the rule file. What is the essential difference between writing rules in the code?

As we mentioned earlier, business rules can be managed dynamically when using the rule engine. Business personnel can manage business rules like managing data, such as querying, adding, updating, statistics, submitting business rules, etc. In this way, the business rules can be adjusted without restarting the service.

3.3 summary

3.3.1 composition of rule engine

The drools rule engine consists of the following three parts:

  • Working Memory
  • Rule Base
  • Inference Engine

The Inference Engine includes:

  • Pattern Matcher
  • Agenda
  • Execution Engine

3.3.2 description of relevant concepts

Working Memory: Working Memory. The drools rule engine will get data from Working Memory and match the pattern with the rules defined in the rule file. Therefore, the application we developed only needs to insert our data into Working Memory. For example, in this case, we call kieSession.insert(order) to insert the order object into Working Memory.

Fact: fact means that in the application of drools rules, the object after inserting an ordinary JavaBean into Working Memory is a fact object. For example, the Order object in this case belongs to a fact object. Fact object is a bridge or channel for data interaction between our application and rule engine.

Rule Base: Rule Base. The rules defined in the rule file will be loaded into the Rule Base.

Pattern Matcher: a matcher that matches all rules in Rule Base with the Fact object in Working Memory. The rules that match successfully will be activated and put into the Agenda.

Agenda: agenda, which is used to store the rules activated after pattern matching through the matcher.

Execution Engine: Execution Engine, which executes the activated rules in Agenda.

Tags: Java Back-end drools

Posted on Thu, 04 Nov 2021 03:23:03 -0400 by The.Pr0fess0r