6 min read

Domain Specific Language

The domain in this sense represents the business area (for example, life insurance or billing). Rules are expressed with the terminology of the problem domain. This means that domain experts can understand, validate, and modify these rules more easily.

You can think of DSL as a translator. It defines how to translate sentences from the problem-specific terminology into rules. The translation process is defined in a .dsl file. The sentences themselves are stored in a .dslr file. The result of this process must be a valid .drl file.

Building a simple DSL might look like:

[condition][]There is a Customer with firstName 
{name}=$customer : Customer(firstName == {name})
[consequence][]Greet Customer=System.out.println("Hello " +
$customer.getFirstName());

Code listing 1: Simple DSL file, simple.dsl.

The code listing above contains only two lines (each begins with [). However, because the lines are too long, they are wrapped effectively creating four lines. This will be the case in most of the code listings.
When you are using the Drools Eclipse plugin to write this DSL, enter the text before the first equal sign into the field called Language expression, the text after equal sign into Rule mapping, leave the object field blank, and select the correct scope.

The previous DSL defines two DSL mappings. They map a DSLR sentence to a DRL rule. The first one translates to a condition that matches a Customer object with the specified first name. The first name is captured into a variable called name. This variable is then used in the rule condition. The second line translates to a greeting message that is printed on the console. The following .dslr file can be written based on the previous DSL:

package droolsbook.dsl;
import droolsbook.bank.model.*;
expander simple.dsl
rule "hello rule"
when
There is a Customer with firstName "David"
then
Greet Customer
end

Code listing 2: Simple .dslr file (simple.dslr) with rule that greets a customer with name David.

As can be seen, the structure of a .dslr file is the same as the structure of a .drl file. Only the rule conditions and consequences are different. Another thing to note is the line containing expander simple.dsl. It informs Drools how to translate sentences in this file into valid rules. Drools reads the simple.dslr file and tries to translate/expand each line by applying all mappings from the simple.dsl file (it does it in a single pass process, line-by-line from top to bottom). The order of lines is important in a .dsl file. Please note that one condition/consequence must be written on one line, otherwise the expansion won’t work (for example, the condition after the when clause, from the rule above, must be on one line).

When you are writing .dslr files, consider using the Drools Eclipse plugin. It provides a special editor for .dslr files that has an editing mode and a read-only mode for viewing the resulting .drl file. A simple DSL editor is provided as well.

The result of the translation process will look like the following screenshot:

Human-readable Rules with Drools JBoss Rules 5.0(Part 1)

This translation process happens in memory and no .drl file is physically stored. We can now run this example. First of all, a knowledge base must be created from the simple.dsl and simple.dslr files. The process of creating a package using a DSL is as follows (only the package creation is shown)

Human-readable Rules with Drools JBoss Rules 5.0(Part 1)

KnowledgeBuilder acts as the translator. It takes the .dslr file, and based on the .dsl file, creates the DRL. This DRL is then used as normal (we don’t see it; it’s internal to KnowledgeBuilder). The implementation is as follows:

private KnowledgeBase createKnowledgeBaseFromDSL()
throws Exception {
KnowledgeBuilder builder =
KnowledgeBuilderFactory.newKnowledgeBuilder();
builder.add(ResourceFactory.newClassPathResource(
"simple.dsl"), ResourceType.DSL);
builder.add(ResourceFactory.newClassPathResource(
"simple.dslr"), ResourceType.DSLR);
if (builder.hasErrors()) {
throw new RuntimeException(builder.getErrors()
.toString());
}

KnowledgeBase knowledgeBase = KnowledgeBaseFactory
.newKnowledgeBase();
knowledgeBase.addKnowledgePackages(
builder.getKnowledgePackages());
return knowledgeBase;
}

Code listing 3: Creating knowledge base from .dsl and .dslr files.

The .dsl and subsequently the .dslr files are passed into KnowledgeBuilder. The rest is similar to what we’ve seen before.

DSL as an interface

DSLs can be also looked at as another level of indirection between your .drl files and business requirements. It works as shown in the following figure:

Human-readable Rules with Drools JBoss Rules 5.0(Part 1)

The figure above shows DSL as an interface (dependency diagram). At the top are the business requirements as defined by the business analyst. These requirements are represented as DSL sentences (.dslr file). The DSL then represents the interface between DSL sentences and rule implementation (.drl file) and the domain model. For example, we can change the transformation to make the resulting rules more efficient without changing the language. Further, we can change the language, for example, to make it more user friendly, without changing the rules. All of this can be done just by changing the .dsl file.

DSL for validation rules

We’ll rewrite the three usually implemented object/field required rules as follows:

  • If the Customer does not have an address, then Display warning message
  • If the Customer does not have a phone number or it is blank, then Display error message
  • If the Account does not have an owner, then Display error message for Account

We can clearly see that all of them operate on some object (Customer/Account), test its property (address/phone/owner), and display a message (warning/error) possibly with some context (account). Our validation.dslr file might look like the following code:

expander validation.dsl

rule "address is required"
when
The Customer does not have address
then
Display warning
end

rule "phone number is required"
when
The Customer does not have phone number or it is blank
then
Display error
end

rule "account owner is required"
when
The Account does not have owner
then
Display error for Account
end

Code listing 4: First DSL approach at defining the required object/field rules (validation.dslr file).

The conditions could be mapped like this:

[condition][]The {object} does not have {field}=${object} : {object}( 
{field} == null )

Code listing 5: validation.dsl.

This covers the address and account conditions completely. For the phone number rule, we have to add the following mapping at the beginning of the validation.dsl file:

[condition][] or it is blank = == "" ||

Code listing 6: Mapping that checks for a blank phone number.

As it stands, the phone number condition will be expanded to:

$Customer : Customer( phone number == "" || == null )

Code listing 7: Unfinished phone number condition.

To correct it, phone number has to be mapped to phoneNumber. This can be done by adding the following at the end of the validation.dsl file:

[condition][]phone number=phoneNumber

Code listing 8: Phone number mapping.

The conditions are working. Now, let’s focus on the consequences. The following mapping will do the job:

[consequence][]Display {message_type} for {object}={message_type}( 
kcontext, ${object} );
[consequence][]Display {message_type}={message_type}( kcontext );

Code listing 9: Consequence mappings.

The three validation rules are now being expanded to the .drl representation

LEAVE A REPLY

Please enter your comment!
Please enter your name here