Getting started with Business Rules (Drools Rule Language)

In this exercise you will build a decision microservice which determines whether a person is an adult. To implement this service you will use Kogito and Quarkus, and develop these rules using DRL (Drools Rule Language), the rule unit API and the OOPath rule syntax.

Let’s get started.

Pre-requisites

  • Visual Studio Code

  • JDK 11+

  • Maven 3.6.3+

  • cURL (or another client/tool with which RESTful requests can be sent to the Kogito application)

Create the service project

In this step, you will create a Kogito application skeleton with Quarkus as the execution runtime.

The easiest way to create a new Kogito service is to use Quarkus maven plugin, as you can see in the command below:

mvn io.quarkus.platform:quarkus-maven-plugin:2.8.2.Final:create \
  -DprojectGroupId=org.acme \
  -DprojectArtifactId=rules-lab01 \
  -Dextensions="kogito-quarkus-rules,quarkus-smallrye-openapi,quarkus-smallrye-health,quarkus-resteasy-jackson" \
  -DnoCode

This command should generate a basic Maven project for you in the rules-lab01 subdirectory. The project consists of:

Once the project is generated, open the project in Visual Studio Code:

$ cd rules-lab01
$ code .

Running the Application

We will now run the rules service using Quarkus development mode. This allows us to keep the application running while implementing our application logic. Kogito and Quarkus will hot reload the application when it is accessed and changes have been detected.

Go back to your terminal (or open the integrated terminal in Visual Studio Code).

VSCode POM

Make sure that you’re in the root directory of the adult-service project (the directory containing the pom.xml file). We are ready to run our application. Run the following command to start the application in Quarkus development mode:

mvn clean compile quarkus:dev

Once the application is up, you can access the Swagger UI

You should be able to see the following page:

Empty Swagger UI

It’s working!

You can now stop the application with CTRL-C.

Congratulations!

You’ve seen how to create the skeleton of basic rules service using Kogito and Quarkus, and how to start the application in Quarkus dev-mode.

Creating the Domain Model

In the previous step we’ve created a skeleton Kogito application with Quarkus and started the application in Quarkus dev-mode. In this step we create the domain model of our application.

Facts

A (business) rules and/or decision service operates on entities called facts. Facts is data over which a rules engine reasons and to which it applies its constraints. In Kogito, facts are implemented as POJOs (Plain Old Java Objects).

Our adult service determines if a person is an adult based on his age.

From this description of our application, we can infer the fact:

  • Person: which has a name, an age, and a boolean that states whether he/she is an adult.

Person

We first implement the Person class. To do this, we first need to create a new package in our project.

In your Visual Studio Code IDE, create the package src/main/java/org/acme/domain

Create domain package

In the package src/main/java/org/acme/domain create a new Person.java file.

Implement this class as follows:

package org.acme.domain;

public class Person {

    private String name;

    private int age;

    private boolean adult;

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public boolean isAdult() {
        return adult;
    }

    public void setAdult(boolean adult) {
        this.adult = adult;
    }

}

image::drl/vscode-create-person-java.png[Create Person.java]

Congratulations!

You’ve implemented the domain model of your Kogito business rules project. In the next step, we will implement the Rule Unit of our application.

Developing rules with Rule Units

Rule Units are groups of data sources, global variables, and DRL rules that function together for a specific purpose. You can use rule units to partition a rule set into smaller units, bind different data sources to those units, and then execute the individual unit.

Create a PersonUnit

We first implement the skeleton of our PersonUnit class. To do this, we first create a new PersonUnit.java file in the org.acme package in src/main/java

PersonUnit Java

Implement this new PersonUnit class as follows:

package org.acme;

import org.acme.domain.Person;
import org.kie.kogito.rules.DataSource;
import org.kie.kogito.rules.DataStore;
import org.kie.kogito.rules.RuleUnitData;

public class PersonUnit implements RuleUnitData {

//Add Person DataStore here

//Add adultAge variable here

    public PersonUnit() {

    }

//Add DataStore Getters and Setters here

//Add adultAge Getters and Setters here

}

Notice that the class is not fully implemented yet. We will add the additional logic now.

We now need to create our DataSource for our Person facts. A DataSource provides us with a typed API to add facts to our unit. Kogito provides a number of different DataSources types, for example a DataStore, which allows users to insert, update and remove facts, and a DataStream that only allows to append facts to a stream.

In this example we will be using the DataStore implementation for our Person facts. We therefore add a private DataStore variable to our rule unit. We use the DataSource factory class to create a new DataStore instance and assign it to the variable. Add the following code snippet to the PersonUnit.java class, at the place of the //Add Person DataStore here comment:

  private DataStore<Person> persons = DataSource.createStore();

We also create the getters and setters for our store. Add the following code snippet to the PersonUnit.java class, at the place of the //Add DataStore Getters and Setters here comment:

  public DataStore<Person> getPersons() {
      return persons;
  }

  public void setPersons(DataStore<Person> persons) {
      this.persons = persons;
  }

That’s it for now. We will implement some additional functionality to this unit later in this lab.

Congratulations!

In this step you’ve implemented your first Rule Unit. Well done! In the next step we will implement the rules and queries of our rule unit.

Enhancing the rules

The rules of our rule unit will be implemented in DRL, the Drools Rule Language. DRL is a declarative language in which advanced rules can be defined and implemented, using constructs like rules, functions and queries.

PersonUnit DRL

We first implement the skeleton of our PersonUnit.drl file in the src/main/resources/org/acme directory of our project.

New resources package

Implement this DRL file as follows:

package org.acme;
//Unit definition

import org.acme.domain.Person;

rule "Is Adult"
when
//Person OOPath
then
//Set adult
end

query "adult"
//Adult query
end

We first need to define that this PersonUnit.drl is connected to our PersonUnit. We do this through unit definition under the package definition at the top of the DRL file: Add the following DRL snippet to the PersonUnit.drl file, at the place of the //Unit definition comment:

unit PersonUnit;

Next, we implement the constraint, or left-hand-side of our rule. We will do this in the OOPath syntax. OOPath allows us to write constraints in an XPath-like syntax, allowing users to more easily navigate object hierarchies when writing rules. Also, it allows us to easily define constraints using the rule unit DataSource paradigm.

The following constraint matches Person facts from the persons datastore of our unit, who’s age is equal to, or greater than 18. Add this snippet to the DRL file, at the place of the //Person OOPath comment.

  $p: /persons[age >= 18];

We can now implement the consequence of our rule, or the right-hand-side (RHS). This the action that will be executed when the rule fires. In our case we want to set the person’s adult field to true when the rule fires. Add this snippet to the DRL file, at the place of the //Set adult comment.

  $p.setAdult(true);

The next thing we need to do for our Kogito application is a query. The query in a unit’s DRL, in combination with the rule unit definition, is used by the Kogito code generator to automatically generate the RESTful endpoint for our application. In this query, we simply want to return all the facts from our persons datastore. Add this snippet to the DRL file, at the place of the //Adult query comment.

  $p: /persons;

This completes the initial implementation of our DRL.

Running the application

With our domain model, rule unit and rules implemented, we can now start our application. In a terminal, execute the following Maven command.

$ mvn clean compile quarkus:dev

We can inspect the generated RESTful endpoint in the Swagger-UI of the application.

Empty Swagger UI

We can now send a request to our generated RESTful endpoint using cURL:

curl -X POST "http://localhost:8080/adult" -H \
"accept: application/json" -H "Content-Type: application/json" \
-d "{\"persons\":[{\"age\":18,\"name\":\"Jason\"}]}"

You should see the following result, showing that Jason is an adult:

[{"name":"Jason","age":18,"adult":true}]

Stop the application in the first terminal using CTRL-C.

Congratulations

In this step you’ve implemented your first Kogito rules and queries. You’ve seen how Kogito automatically generates the RESTful microservice for you using your business assets, like your rule unit and rules definitions. Finally, we’ve started our application in Quarkus dev-mode, and fired a request.

Working with variables in Rule Units

Apart from using DataSources in our rule units to insert, update and delete facts, we can also define variables in our unit that can be used in our rules. In this use-case we will add an adultAge variable to our unit, which allows us to send the age at which a person is considered an adult in our request, and using that age in our rules.

PersonUnit DRL

First, we add a new adultAge variable to our PersonUnit class. Open the PersonUnit.java file and add the following code snippet at the //Add adultAge variable here comment.

  private int adultAge;

We also add the getters and setters. Add these at the //Add adultAge Getters and Setters here comment in the PersonUnit.java class.

  public int getAdultAge() {
      return adultAge;
  }

  public void setAdultAge(int adultAge) {
      this.adultAge = adultAge;
  }

With our variable implemented, we can now use this variable in our rules. Open the PersonUnit.drl file, and replace the constaint of the rule ($p: /persons[age >= 18];) with the following constraint, which replaces the hardcoded age 18 with our variable:

  $p: /persons[age >= adultAge];

We’ve now added the functionality we want, so we can start our application again. Execute the following Maven command in a terminal:

mvn clean compile quarkus:dev

We can now hit the application with a request that contains our new adultAge variable:

$ curl -X POST "http://localhost:8080/adult" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"adultAge\": 21, \"persons\":[{\"age\":18,\"name\":\"Jason\"}]}"

This will give you the following result:

[{"name":"Jason","age":18,"adult":false}]

Notice that, because we have defined the adultAge to be 21, Jason is no longer considered an adult.

OpenAPI Specification

A Kogito Quarkus application running in Quarkus dev-mode automatically exposes an OpenAPI specification of its RESTful resources through a Swagger-UI. You can open this Swagger-UI using this link.

Open the POST /adult RESTful endpoint. Note that a fully typed API is generated for you, based on your business assets like your rule units and rules.

Kogito Adult Service POST API

Congratulations

You have added a variable to your rule unit and used it in your rules. You’ve also experienced the power of live/hot reload of Kogito, providing extremely fast roundtrip times.

In this lab, you’ve learned how to implement business rules using Rule Units in Kogito. We’ve seen how Kogito generates a RESTful microservice from business assets, like your rule unit and rules. We’ve experienced the power of Kogito hot-reload when changing rules.