There are some questions that stubbornly refuse to succumb to popular analysis, “How long is a piece of string”, “Is that data or meta-data?” and today I’d like to propose another, specific to business rules, “Is that policy or meta-policy?”
I informally define meta-policy as, "Business rules (captured explicitly, or implicit in an application implementation) that are used to determine the set of business rules to apply to an incoming transaction."
Imagine an insurance company that offers products to customers across several countries or states. When a request for an insurance quote is made by a prospective customer through the insurance company’s web portal the automated quoting system has to determine the correct pricing rules to fire (activate) based on the characteristics of the customer. Perhaps some surcharge pricing rules are specific to high crime states while other rules provide a discount to female customers over the age of 45. The goal of the BRMS is to allow the Policy Manager to easily express and manage the rules for the insurance products, as well as execute the correct rules at runtime.
Clearly there is one logical set of real business rules however the rules may be implemented using several possible physical strategies. Typically these strategies are determined by a pragmatic trade-off between manageability, extensibility, performance as well as how the insurance company has traditionally talked about and managed their business. I hope some examples will clarify!
Consider the following very simple Action Rule created using ILOG JRules:
The rule has been placed within a package (folder) called surcharge:
Nice and simple and atomic isn’t it? However as the number of states increases it is quite likely that the Policy Manager will want to organize the business rules by geography, allowing them to quickly view the rules that are specific to New York. However the information about “New York” is then duplicated: once in the package hierarchy and once within the condition of the rule. E.g.
The Policy Manager does not like the fact that they have to explicitly add the
“the state of customer is "New York” for rules in the New York package as they rightly believe that it is redundant with the package organization, as well as error prone if they should copy/paste or move a business rule. We’ve fallen back into the “data vs. meta-data” debate! The package organization of a rule is meta-data whereas the rule condition is data. It is extremely useful to be able to use the package meta-data as an implicit condition on the rules. Of course one of the drawbacks is that it may be too implicit, detracting from the atomicity and reusability of the rule outside its owning package.
I will now discuss some ILOG JRules mechanisms and patterns that can be useful when dealing with meta-policy.
Determining the rules within a ruleset
First however I must digress a little and explain how you can specify the contents of an executable ruleset archive. By default all the rules within a Rule Project are placed within the ruleset archive. However in some situations it can be useful to filter or select the rules based on their properties (meta-data) using a query:
Here “effective date” is an out-of-the-box rule property; however any custom property can be used in rule queries. Using the query above only the rules within the "New York" package would be extracted to the generated ruleset archive.
Explicit rule conditions
As I stated earlier, the natural thing to do is to capture all rule conditions explicitly on the left-hand-side of the rule (in the “if” part). This has the very nice property that it likely makes the rules atomic so they can be safely moved around the Rule Project and a single rule can be read and analyzed independent of its context. The drawback is that if there are many repeated tests (but the rules are not amenable to a decision table or tree) the rules can appear verbose and somewhat clumsy.
It is often the case that the meta-policy states that under certain conditions a group of rules should override or take precedence over another set of rules. This is commonly the case when dealing with a hierarchical property such as geographical regions for example. For this case JRules provides a declarative mechanism to specify that a rule or decision table/tree should override a set of rules or decision tables/trees.
For relatively simple routing of a transaction to rules it is possible to use a Ruleflow with a Choice Node and a guard condition. The ruleflow implements the meta-policy, while the rules in the “New York” and “Maine” packages no longer have to test the state of the customer as it has already been performed by the ruleflow.
Taken too far however this approach can lead to maintenance problems as the procedural Ruleflows can get very complex and hard to maintain, even if broken into sub-flows. Large ruleflows can also become relatively expensive to execute as they must typically be evaluated sequentially from start-to-finish as compared to a declarative pattern-matching based approach using rules.
One can be even more drastic and decide to create separate Rule Projects for each state of interest, perhaps using Rule Project dependencies to a sub-project to import rules that are independent of state. Alternatively one could use a set of queries to build different rulesets from a single Rule Project, filtering the rules based on their meta-data.
At runtime the application first determines the correct ruleset to execute, based on the customer’s state, and then invokes the Rule Execution Server or rule engine. Execution may be moderately faster because the state of the customer is no longer tested at all within the ruleset.
Deploying multiple rulesets can also be useful is there are a very large number of rules that are extremely disjoint or that have different life-cycles or Quality of Service objectives. A frequently updated ruleset might be split out from a very large ruleset to improve deployment and parsing time for example.
A Rule Execution Server Ruleset Interceptor can be used to register a Java class that is automatically called prior to invoking the ruleset. The interceptor can determine the correct ruleset to call (routing) based on data within the incoming transaction as well as meta-data on the deployed rulesets.
One may also decide to use a custom property associated with business rules. Adding a custom property “state” to every rule, avoiding the need to test the state condition on every state specific rule. The "state" property will safely follow the rule as it is moved or copied between packages. The state property can be used by a query that builds the ruleset, for runtime rule selection or rule rewriting (see below).
The package name is often used for meta-policy (either using a query to build the ruleset, a ruleflow to route between packages or using runtime rule selection). Although very convenient, implementing meta-policy using rule packages requires some care, as the rules within a package may now rely on an implicit condition.
Runtime rule selection
Runtime rule selection is a very powerful mechanism that allows the rule engine to determine the rules within a rule task at runtime. The ruleflow definition becomes trivial:
While the definition of the “State Rules” task contains the Runtime Selection logic:
The “dynamic” checkbox can be used to ensure the rules eligible for a task are recomputed each time the task is entered. For rule tasks that do not use dynamic runtime rule selection the computation is performed once, when the rule flow is instantiated by the rule engine.
Rule rewriting is an advanced mechanism to implement meta-policy. By customizing the translation of Action Rules to executable IRL rule engine code you can automatically insert conditions or actions into the rules based on their meta-data. I would not typically recommend this option as it is relatively difficult to implement and is very opaque to the business users and developers that have to maintain the business rules application.
The agenda is another advanced mechanism that has historically been used in some cases to implement meta-policy. Agenda filters are a procedural “hook” of last resort that allows custom code to prevent a rule from firing that has been selected by the rule engine (i.e. all the rule's conditions are true). The drawback with agenda filters is that they are again very opaque to the business users of the rules, require either IRL or Java code to implement, and are not optimal from a performance perspective, because the rule engine has already done all the work to determine the rule should be fired, only to have that decision overruled by the agenda filter. One of their advantages is that they operate upon rule instances allowing a single rule to be potentially prevented from firing based on the state of the objects that caused the rule to become matched, or even the other rules that have been selected for execution and are present in the agenda.
I’ve tried to summarize the JRules meta-policy options in the table below. Like most generalizations it doesn’t stand up to too close scrutiny but I hope it will kick start some discussion on how to best determine the rules to apply to incoming transactions, while making smart trade-offs between manageability, performance and dynamicity. Note that these patterns are not mutually exclusive -- you can combine the extraction of multiple rulesets with runtime rule selection within each ruleset for example.