Drools Rule Example Walkthrough

Drools is a powerful, open-source rule engine built on Java that separates volatile business logic from core application code. By managing requirements through declarative DRL (Drools Rule Language) files, it achieves a clean decoupling of business rules from software implementation. This article explores the syntax structure and execution mechanics of Drools through a comprehensive order-discount scenario.

A standard Drools rule consists of a rule name, optional attributes, a Left-Hand Side (LHS) for conditions, and a Right-Hand Side (RHS) for actions. Its structural skeleton appears as follows:

package rules

import com.demo.model.Order

rule "Rule Name"
    salience 10          // Attribute section
    when
        // LHS: Fact pattern matching
    then
        // RHS: Business action execution
end
  • package: Defines the namespace for the rule file and must be placed on the first line.
  • import: Imports necessary Java classes for use within the rules.
  • rule ... end: Encloses the rule definition. Attributes reside before when, conditions between when and then, and executable actions between then and end.
    Consider the following business requirements:
  1. Orders exceeding 1,000 currency units receive a 10% discount.
  2. VIP members receive an additional 50-unit discount on top of the initial reduction.

First, define the fact object in Java:

public class Order {
    private double amount;    // Order total amount
    private boolean vip;      // VIP status flag
    private double discount;  // Calculated discount amount
    // Getters and setters omitted for brevity
}

The corresponding DRL rules are structured as follows:

package rules

import com.demo.model.Order

rule "Large Order Ten Percent Off"
    salience 20
    when
        $o : Order(amount > 1000, discount == 0)
    then
        $o.setDiscount($o.getAmount() * 0.1);
        update($o);
end

rule "VIP Additional Fifty Off"
    salience 10
    no-loop true
    when
        $o : Order(vip == true, discount > 0)
    then
        $o.setDiscount($o.getDiscount() + 50);
        update($o);
end

The Matching Syntax of the Condition Section (LHS)

The LHS comprises one or more patterns used to match fact objects residing inside the Working Memory:

  • In $o : Order(amount > 1000, discount == 0), $o serves as a binding variable, making it easy to reference the matched object in the action block.
  • Multiple constraints inside parentheses default to an and logical relationship, though explicit operators like && and || are also supported.
  • Conditional elements like not and exists are supported. For instance, to verify if a high-value order exists in the Working Memory:
when
    exists Order(amount > 10000)
then
    // Execute corresponding logic

The Action Section (RHS) and Inference Control

The RHS executes a block of Java code once a rule matches and activates. Beyond standard business logic, it can invoke built-in engine methods to modify the state of facts:

  • update($o): Notifies the engine that the object has changed, triggering re-matching for related rules.
  • insert(new Xxx()): Inserts a brand-new fact into the Working Memory.
  • retract($o): Removes a fact from the Working Memory.

Care must be taken with update, as it triggers re-evaluation. In our example, the "VIP Additional Fifty Off" rule modifies the discount field, but since the condition discount > 0 remains true afterward, the rule could repeatedly activate itself and cause an infinite loop. Using no-loop true prevents this. Alternatively, the modify block is recommended because it merges property changes and engine notifications into a single, clean operation:

then
    modify($o) {
        setDiscount($o.getDiscount() + 50)
    }
end

Executing Rules in Java

KieServices ks = KieServices.Factory.get();
KieContainer kc = ks.getKieClasspathContainer();
KieSession session = kc.newKieSession("orderKS"); // Corresponds to kmodule.xml configuration

Order order = new Order();
order.setAmount(1500);
order.setVip(true);

session.insert(order);
session.fireAllRules();
session.dispose();

System.out.println("Discount amount: " + order.getDiscount()); // Outputs 200.0

The execution flow proceeds as follows: facts are inserted into the session, the engine performs pattern matching on all rules to generate activations, and rules fire in descending order of salience. In the sample case, the order first triggers the ten-percent-off rule for a 150-unit discount, and subsequent updates activate the VIP rule for an extra 50 units, yielding a final discount of 200 units.

Common Rule Attributes and Best Practices

  • salience: Higher numerical values represent higher execution priorities, governing the sequence of rule evaluation.
  • no-loop: Prevents a rule from re-activating due to modifications made by its own execution.
  • date-effective / date-expires: Restricts rule validity within specific time windows.
  • agenda-group / activation-group: Facilitates rule partitioning and mutually exclusive execution blocks.

Best practices recommend dividing rule files by business domain with semantic naming conventions. The RHS should focus strictly on simple assignments and state transitions, avoiding complex procedural workflows. Additionally, writing comprehensive unit tests for critical rules ensures that system behavior remains verifiable after adjustments.

Through these concepts, Drools organizes business logic via a pattern-matching and inference-execution paradigm. Combined with mechanisms like salience, update, and no-loop, it offers a flexible, highly controllable approach to managing complex business rules.