Fact Type Hierarchies in Drools
Although Drools is Java-oriented and provides the ability to handle facts in an object-oriented manner thanks to the ReteOO optimized algorithm, there is always room for improvement. Drools 5.3+ offers us Traits for solving some emergent limitations of Drools: Fact Type hierarchies.
This article describes an experimental new Drools Rule Language feature in Drools 5.3.
Consider that we are working on a component that analyses facts with the intention of classifying/flagging/tagging them so that other rules may or may not fire. We essentially want an interface for such facts, and one fact may have multiple such interfaces attached to it, so how do we do that? Traditionally designers would use shadow facts for such a thing, modified copies of an existing fact, and spend most of their time solving the headache that comes from keeping the tons of different shadow/proxy facts in sync with their originals.
The experimental solution
Fortunately we now have access to an experimental feature called Traits. The idea is to provide a mechanism that can apply and remove an interface to an object at runtime. Upon creating a trait, based on a bean interface, a new proxy class is created on the fly. In fact, one proxy class is lazily generated for each combination of trait and fact-class. A proxy class instance will wrap core fact and implement the interface. As proxy instances can be combined, and directly called and manipulated, they provide a powerful in-memory method for complicated transactions and ultimately more efficiency in firing your rules.
We’ll use the following fact model:
Now, you can see that our core facts (Employee, PrivateJet) are supplemented by the @Traitable annotation. And we declared three additional facts for the proxy classes using the @format(trait) annotation. Notice that the traits share some of their fields with the traitable facts. When a trait is mapped to a traitable, the getters and setters of shared fields are remapped internally to the fields of the traitable automatically. Any core object fields that are not mapped in a trait, will be invisible to the trait.
Mapping an interface to a fact is quite easy, with the don keyword:
Now other rule may be able to fire:
Notice that the last example only propagates for an Employee that has both the Senior and Reviewable traits mapped. The isA operator provides easy cross-matching on Traits, although in this particular example it also would have been possible to match on the "wasReviewed" attribute of the Reviewable trait in exactly the same manner as the matching on 'code'.
Removing a trait is simple as well, using the shed operator:
It seems that Traits as offered in this shape are a potentially quite powerful and useful feature. Because Traits streamline the generation of proxy classes without causing wide-scale data duplication, they provide developers and business experts with an effective method to do what they have been wanting to do for years. As a result, the performance of complicated systems may be increased greatly while reducing the risk of errors.
However, I am not yet convinced that it actually has a positive effect on the general readability of rules, or the maintainability of DSL supplements. As such, I definitely wouldn’t mind seeing this feature at work in a more complicated project.
Since the feature is also still 'experimental', it may be too soon to judge the syntax. Fortunately it does look quite promising.