Skip to main content

DRL

DRL — the Drools Rule Language — is the native language of the Drools rule engine. Where DMN gives business analysts a visual, standardized notation, DRL gives developers the full expressive power of the engine in plain text files that live in your source tree, next to your Java code, under version control.

A rule says when something is true, then do something. You never call a rule; you give the engine facts and it works out which rules match, in what order, and re-evaluates as those facts change.

A first rule​

package org.acme.orders

import org.acme.orders.Order

rule "Free shipping over 100"
when
$order : Order( total > 100, freeShipping == false )
then
modify( $order ) { setFreeShipping( true ) };
end

when holds the conditions — patterns matched against the facts the engine knows about. Order( total > 100 ) reads as "there is an Order whose total is greater than 100"; $order binds the match so the then block can use it. modify changes a fact and tells the engine it changed, so any other rule that cares about freeShipping gets reconsidered. That is the part that makes a rule engine different from a pile of if statements: rules react to each other, and you never wrote the control flow.

Rule units​

A loose bag of rules and a shared session works, but it gets hard to reason about at scale. Rule units are the modern way to organize DRL: a unit is a typed container that declares the data its rules operate on, so the rules and their inputs are one cohesive, injectable thing.

The data holder is a plain Java class:

public class HelloWorldUnit implements RuleUnitData {

private final DataStore<String> strings = DataSource.createStore();
private final List<String> results = new ArrayList<>();

public DataStore<String> getStrings() { return strings; }
public List<String> getResults() { return results; }
}

The DRL binds itself to that unit and navigates its data sources with OOPath expressions — the /strings[ ... ] syntax:

package org.acme;
unit HelloWorldUnit;

rule HelloWorld
when
/strings [ this == "Hello World" ]
then
results.add("it worked!");
end

And running it is three lines, with no session bootstrapping to speak of:

HelloWorldUnit unit = new HelloWorldUnit();
unit.getStrings().add("Hello World");

try (RuleUnitInstance<HelloWorldUnit> instance =
RuleUnitProvider.get().createRuleUnitInstance(unit)) {
instance.fire();
}

Rule units are also what Kogito generates REST endpoints from, so the same unit you unit-test locally becomes a decision service without rewriting it.

Beyond simple matching​

DRL is a language, not a table format, and the interesting parts show up once the conditions stop being row-shaped.

Absence is a first-class condition. not, exists and forall let you match on facts that aren't there — something no lookup table expresses naturally:

rule "Flag unreviewed high-value orders"
when
$order : Order( total > 10000 )
not Review( order == $order )
then
$order.flagForReview();
end

Aggregation over the whole working memory, via accumulate:

rule "Bulk discount"
when
$customer : Customer()
Number( intValue >= 10 ) from accumulate(
$o : Order( customer == $customer, status == "OPEN" ),
count( $o ) )
then
$customer.applyBulkDiscount();
end

Queries ask the engine questions instead of firing actions, and they can recurse — which is how you get backward chaining:

query isContainedIn( String x, String y )
/locations[thing := x, location := y]
or
( /locations[z := thing, location := y] and isContainedIn(x, z;) )
end

Truth maintenance: facts inserted logically are retracted automatically when the reason for them stops holding, so you never write the cleanup pass.

Execution control when you need it — salience to order rules, no-loop to stop a rule re-triggering itself, agenda-group to partition the agenda into phases.

Events and time​

Declare a type as an event and the same language becomes a complex event processing engine. Events carry timestamps and durations, they can be reasoned about with temporal operators, and they expire on their own:

declare TemperatureReading
@role( event )
end

rule "Sustained overheating"
when
$r : TemperatureReading( celsius > 90 ) over window:time( 10s )
from entry-point SensorStream
then
alerts.add( new Alert( $r ) );
end

over window:time( 10s ) slides a ten-second window over the stream, and from entry-point keeps that stream partitioned from the rest of working memory. Because events expire, long-running streams don't grow the session without bound.

Other ways to write the same rules​

DRL text is one surface. The same semantics are reachable through decision tables in Excel (drools-decisiontables), rule templates, and the executable model API for building rules programmatically in Java — all of which compile down to the same engine.

Rules also compose with the rest of the platform: a BPMN business rule task in jBPM calls straight into a DRL rule set, in the same service, with no remote hop.

Resources​

Drools documentation: the DRL language reference is the complete specification — every keyword, operator and conditional element. The rule engine guide covers how matching, the agenda and truth maintenance actually work underneath.

Ready to write some? Get started.