Spring Boot + rule engine Drools, strong!

Hello everyone, I am Bucai Chen~

Alipay sent a red envelope, copy: 817180432 Go to Alipay to search and get it, ranging from 1 yuan to 20 yuan per person

Now there is such a demand, online shopping needs to calculate product discounts according to different rules, such as 5% discount for VIP customers, 10% discount for purchases exceeding 1,000 yuan, etc., and these rules may change at any time, or even increase new rules. Faced with this demand, how do you realize it? Could it be that when the calculation rules change, the business code must be modified, retested, and launched.

In fact, we can implement it through a rule engine. Drools is an open source business rule engine that can be easily integrated with spring boot applications. In this article, we will use Drools to realize the above-mentioned requirements.

Regarding the rule engine, there are two previous articles that introduced it, as follows:

  • In-depth comparison of rule engines, LiteFlow vs Drools!

  • Talk about the small and beautiful rule engine LiteFlow

Other rule engines will be introduced later……

Introducing dependencies

We create a spring boot application and add drools-related dependencies to the pom, as follows:

<dependency>
  <groupId>org.drools</groupId>
  <artifactId>drools-core</artifactId>
  <version>7.59.0.Final</version>
</dependency>
<dependency>
  <groupId>org.drools</groupId>
  <artifactId>drools-compiler</artifactId>
  <version>7.59.0.Final</version>
</dependency>
<dependency>
  <groupId>org.drools</groupId>
  <artifactId>drools-decisiontables</artifactId>
  <version>7.59.0.Final</version>
</dependency>

Drools configuration class

Create a configuration java class called DroolsConfig.

@Configuration
public class DroolsConfig {
    // specify the path to the rule file
    private static final String RULES_CUSTOMER_RULES_DRL = "rules/customer-discount.drl";
    private static final KieServices kieServices = KieServices.Factory.get();

    @Bean
    public KieContainer kieContainer() {
        KieFileSystem kieFileSystem = kieServices. newKieFileSystem();
        kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_CUSTOMER_RULES_DRL));
        KieBuilder kb = kieServices. newKieBuilder(kieFileSystem);
        kb.buildAll();
        KieModule kieModule = kb. getKieModule();
        KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId());
        return kieContainer;
    }
}
  • A Spring Bean of KieContainer is defined, and KieContainer is used to load the application under the /resources folder rule files to build the rules engine.

  • Creates a KieFileSystem instance and configures the rule engine and loads the rule’s DRL file from the application’s resource directory.

  • Use KieBuilder instance to build drools module. We can use KieServive singleton instance to create KieBuilder instance.

  • Finally, use KieService to create a KieContainer and configure it as a spring bean.

Add business model

Create an order object OrderRequest. The fields in this class are then sent as input information to the defined drools rules to calculate the discount amount for a given customer order.

@Getter
@Setter
public class OrderRequest {
    /**
     * client number
     */
    private String customerNumber;
    /**
     * age
     */
    private Integer age;
    /**
     * order amount
     */
    private Integer amount;
    /**
     * Customer type
     */
    private CustomerType customerType;
}

In addition, define an enumeration of customer type CustomerType from which the rules engine will calculate the customer order discount percentage, as shown below.

public enum CustomerType {
    LOYAL, NEW, DISSATISFIED;

    public String getValue() {
        return this.toString();
    }
}

Finally, create an order discount class OrderDiscount to represent the calculated final discount, as shown below.

@Getter
@Setter
public class OrderDiscount {

    /**
     * Discount
     */
    private Integer discount = 0;
}

We will return the calculated discount using the above response object.

Define drools rules

The previous DroolsConfig class specifies the drools rule directory, now we add customer in the /src/main/resources/rules directory -discount.drl file, which defines the corresponding rules.

c2b0f826f5567fb3efe630a0971b39dd.png

Although this drl file is not a java file, it is still easy to understand.

  • We use a global parameter called orderDiscount that can be shared between multiple rules. Pay attention to Gongzhong account: code ape technology column, reply keywords: 1111 Get Ali’s internal java performance tuning manual!

  • A drl file can contain one or more rules. We can use the mvel syntax to specify rules. Additionally, each rule is described using the rule keyword.

  • Each rule uses when-then syntax to define the conditions of the rule.

  • Based on the input value of the order request, we are adding a discount to the result. Each rule adds an additional discount to the global result variable if the rule expression matches.

The complete rule source code is as follows:

import com.alvin.drools.model.OrderRequest;
import com.alvin.drools.model.CustomerType;
global com.alvin.drools.model.OrderDiscount orderDiscount;

dialect "mvel"

// Rule 1: Judging by age
rule "Age based discount"
    when
        // When the customer's age is under 20 or over 50
        OrderRequest(age < 20 || age > 50)
    then
        // add 10% discount
        System.out.println("===========Adding 10% discount for Kids/ senior customer===============");
        orderDiscount.setDiscount(orderDiscount.getDiscount() + 10);
end

// Rule 2: Rules based on customer type
rule "Customer type based discount - Loyal customer"
    when
        // When the customer type is LOYAL
        OrderRequest(customerType. getValue == "LOYAL")
    then
        // add 5% discount
        System.out.println("==========Adding 5% discount for LOYAL customer===============");
        orderDiscount.setDiscount(orderDiscount.getDiscount() + 5);
end

rule "Customer type based discount - others"
    when
    OrderRequest(customerType. getValue != "LOYAL")
then
    System.out.println("===========Adding 3% discount for NEW or DISSATISFIED customer===============");
    orderDiscount.setDiscount(orderDiscount.getDiscount() + 3);
end

rule "Amount based discount"
    when
        OrderRequest(amount > 1000L)
    then
        System.out.println("===========Adding 5% discount for amount more than 1000$===============");
    orderDiscount.setDiscount(orderDiscount.getDiscount() + 5);
end

Add Service layer

Create a service class called OrderDiscountService, as follows:.

@Service
public class OrderDiscountService {

    @Autowired
    private KieContainer kieContainer;

    public OrderDiscount getDiscount(OrderRequest orderRequest) {
        OrderDiscount orderDiscount = new OrderDiscount();
        // start the session
        KieSession kieSession = kieContainer. newKieSession();
        // set discount object
        kieSession.setGlobal("orderDiscount", orderDiscount);
        // set the order object
        kieSession.insert(orderRequest);
        // trigger rule
        kieSession.fireAllRules();
        // abort the session
        kieSession.dispose();
        return order Discount;
    }
}
  • Inject a KieContainer instance and create a KieSession instance.

  • A global parameter of type OrderDiscount is set, which will save the rule execution result.

  • Use the insert() method to pass the request object to the drl file.

  • Call the fireAllRules() method to fire all rules.

  • Finally, terminate the session by calling the dispose() method of KieSession.

Add Controller

Create a Controller class named OrderDiscountController, the specific code is as follows:

@RestController
public class OrderDiscountController {

    @Autowired
    private OrderDiscountService orderDiscountService;

    @PostMapping("/get-discount")
    public ResponseEntity<OrderDiscount> getDiscount(@RequestBody OrderRequest orderRequest) {
        OrderDiscount discount = orderDiscountService. getDiscount(orderRequest);
        return new ResponseEntity<>(discount, HttpStatus.OK);
    }
}

Test it

Run the spring boot application and access the REST API endpoint by sending the customer order request JSON.

  • For LOYAL customer type with age < 20 and amount > 1000, we should get discount of 20% according to the rules we defined.

ca9a046170a3508aac85d6bd8b2c85ee.png

1449e02b30a1c923b551c0c947708747.png

Summary

We have simply implemented such a discount business through the drools rule engine. Now the product manager asks you to add a rule, for example, if the address is Hangzhou plus 10% discount, you can directly change the drl file. Others Just spend time fishing, haha~~. You can go to the official website to explore more about the usage of drools.

One last sentence (don’t prostitute, please pay attention)

Every article by Chen is carefully output. If this article is helpful or inspiring to you, please like, watch, repost, and bookmark. Your support is the biggest motivation for me to persevere!

In addition, Chen’s Knowledge Planet has been opened, and the official account replies to the key words: Knowledge Planet is limited to 20 yuan coupons to join, only 109 yuan, a meal, but the value of the planet’s feedback is huge, At present, the Spring family bucket actual combat series, the billion-level data sub-database sub-table actual combat, the DDD micro-service actual combat column, I want to enter the big factory, Spring, Mybatis and other framework source code, the architecture actual combat 22 lectures, etc.. Each additional column The price will increase by 20 yuan

31a426d9c84c2225a0ade2cdcab9651f.png

Pay attention to the official account: [Code Ape Technology Column], there are awesome fan benefits in the official account, reply: join the group, you can join the technical discussion group, discuss technology with everyone, and brag!