How to Create a Rule Engine with Drools and Quarkus
There are multiple ways of creating a rule engine, some prefer a DMN model and some like to use DRL (Drools) files. On this writing, we are trying to see how can we leverage DRL files to validate a user’s risk profile based on some variables, and run it on top of Quarkus.
So lets start with a Maven pom file,
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.edw</groupId>
<artifactId>quarkus-drl</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
<quarkus.platform.version>2.15.3.Final</quarkus.platform.version>
<kogito.platform.group-id>org.kie.kogito</kogito.platform.group-id>
<kogito.platform.artifact-id>kogito-quarkus-bom</kogito.platform.artifact-id>
<kogito.platform.version>1.24.0.Final</kogito.platform.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.9.2</version>
</dependency>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>${kogito.platform.group-id}</groupId>
<artifactId>${kogito.platform.artifact-id}</artifactId>
<version>${kogito.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>org.kie.kogito</groupId>
<artifactId>kogito-quarkus-rules</artifactId>
</dependency>
<!-- tests -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
......
</project>
Next is creating a Java model, this is going to be the base model for variables that are going to contribute on our rule engine.
package com.edw.model;
public class Loan {
private Integer age;
private Double salary;
private String risk;
public Loan() {
}
public Loan(Integer age, Double salary, String risk) {
this.age = age;
this.salary = salary;
this.risk = risk;
}
// other setter and getter
}
After that, we need to create a Rule Unit class. The purpose of it is to bind JSON request to a Java class, which can be modified by using our Rule Engine.
package com.edw.queries;
import com.edw.model.Loan;
import org.kie.kogito.rules.DataSource;
import org.kie.kogito.rules.DataStore;
import org.kie.kogito.rules.RuleUnitData;
public class LoanUnitData implements RuleUnitData {
private DataStore<Loan> loan;
public LoanUnitData() {
this(DataSource.createStore());
}
public LoanUnitData(DataStore<Loan> loan) {
this.loan = loan;
}
public DataStore<Loan> getLoan() {
return loan;
}
public void setLoan(DataStore<Loan> loan) {
this.loan = loan;
}
}
Next is to create our DRL file where we can write our whole Rule Engine there, and please make sure that it needs to be on the same package as our RuleUnit class.
package com.edw.queries;
unit LoanUnitData;
import com.edw.model.Loan;
rule HighRiskCustomerEverythingBelowMinimum when
$L: /loan[age <= 20, salary <= 1000]
then
modify($L) { setRisk("High") };
end
rule MediumRiskCustomerAgeBelowMinimum when
$L: /loan[age <= 20, salary > 1000]
then
modify($L) { setRisk("Medium") };
end
rule MediumRiskCustomerSalaryBelowMinimum when
$L: /loan[age > 20, salary <= 1000]
then
modify($L) { setRisk("Medium") };
end
rule LowRiskCustomerSalaryEverythingAboveMinimum when
$L: /loan[age > 20, salary > 1000]
then
modify($L) { setRisk("Low") };
end
query GetRisk
$L: /loan
end
To validate whether our code works well or not, we can use below Unit Test
package com.edw;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems;
@QuarkusTest
public class LoanTest {
@Test
public void testHighRisk() {
given()
.body("{\"loan\" : [{\"age\":17, \"salary\":900}]}")
.contentType(ContentType.JSON)
.log().all()
.when()
.post("/get-risk")
.then()
.statusCode(200).log().all()
.body("risk", hasItem("High"));
}
}
Or we can simply using a CURL to validate this,
$ curl -X POST http://localhost:8080/get-risk \
-H 'content-type: application/json' \
-H 'accept: application/json' \
-d '{"loan" : [{"age":17, "salary":900}, {"age":39,"salary":1900.0}]}'
[{"age":39,"salary":1900.0,"risk":"Low"},{"age":17,"salary":900.0,"risk":"High"}]
Code for this article can be found on below Git repository,
https://github.com/edwin/quarkus-drl
Have fun with Drools 🙂










