Business Rules are pervasive in software.
In fact, in most cases, business rules are the very reason for the existence of most software today. As application architectures have become more and more sophisticated, few can disagree with the merits of separating the presentation layer from the business layer or the data layer from the business layer. Yet many applications today are still built with process logic and business rules interwoven within the same business/application layer, which can lead to applications that are brittle, hard to maintain, and resistant to change. In this article, I will explain how to decouple the business rules within your application in a manner that yields high organizational visibility and accountability; and promotes rules as a unit of reuse to help you build applications that are ready for change.
When Rod Paddock and I first started talking about ideas for this article, my goal was to share some of the techniques I have successfully implemented for exposing the Microsoft Business Rules Engine (BRE) via services. Always the wise pragmatist, Rod suggested that I first start with an introduction. If my experience delivering two talks on the subject this year at Austin Code Camp and Desert Code Camp in Phoenix are any indication, he's probably right. Both sessions were well attended, and most attendees had heard of rule-based engines, but had not taken it much further. This intrigues me, because there is a growing developer community that really understands the benefits of dependency injection, inversion of control, and contract-first development. I think that anyone interested in such techniques should look closer at rule-based engine technology because it is a way to accomplish or, at the very least, compliment many of these techniques. So, while my initial goal of going deeper with the technology will have to wait, I hope that you will discover how valuable (and cost effective) incorporating a rule-based engine into your solution architecture and design can be.
What Are Business Rules?
The Business Rules Group, a non-commercial organization that helps to define and disambiguate the definition of business rules defines a business rule as "a statement that defines or constrains some aspect of the business. It is intended to assert business structure or to control or influence the behavior of the business*."* The Business Rules Group further defines business rules as organizational "guidance that there is an obligation concerning conduct, action, practice, or procedure within a particular activity or sphere*."*
The industry has widely accepted as best practice to separate the data layer from the business layers.
To take a practical example, a business rule may read like this:
** "If the customer is a preferred member, always apply a 10% discount on the total checkout price." **
Take a moment and re-read the Business Rule Group's definition in the first paragraph above. With this perspective, the significance of this rule is profound. With very little knowledge about the business, it is evident that the company sells products or services to individuals, and elite customers who hold a membership status are entitled to receive a 10% discount on purchases. This rule is likely part of an organizational customer loyalty and marketing strategy, as the business rule is almost certainly designed to motivate customers to pay an annual premium for membership status. At a minimum this rule governs the activity of purchasing goods by influencing the behavior of preferred members to purchase higher-dollar purchases since they will be modestly discounted.
This is only one example. Think about the business rules that govern the applications you have built in the past or the project you are currently on. As developers, we wield incredible power-and responsibility-for ensuring that these business rules are elicited, developed, deployed, and maintained as accurately and efficiently as possible. As you can imagine from the example, there are a number of team roles that come into the lifecycle of a business rule, yet as developers we tend to hide these rules in the bowels of code bases that will rarely if ever be visible to other team roles, and when we are asked to change or remove a rule after the application has been deployed we naturally resist. Why!? Because change is hard unless your application is built to evolve, and if there is one area of your application that is almost guaranteed to change, it is the business rules. Business rules need to be dynamic just like the business itself. Rule-based engines allow you to harness this change by making business rules management completely transparent across all interdependent team roles while providing very high run-time performance. In addition to visibility and performance, rule-based engines support the loosely coupled integration of business rules into applications that use them.
Rule Engine Fundamentals
All rule-based engines are similar in that they allow various roles within an organization to author, save, deploy, and interact with business rules in a centralized manner that allows disparate applications to consume the rules in a managed, service-oriented manner. As such, most rule-based engines provide a rule editor, which allows you to author and save business rules; a shared rules repository for persisting and looking up rules; a high-performance rules engine for executing rules in the repository; and administrative tools for deploying, retiring, and migrating rules from one rule repository to another.
How We Got Here
Since the dawn of computing, our forefathers and contemporaries alike have been hard at work chipping away at the same problem: how to break apart the monolith by decoupling as many components as possible without sacrificing performance. Ever since the first computer, the trend has been constant. Computing has moved from sprockets and dials to
assembly languages and onto procedural, object-oriented, component-oriented, and service-oriented designs, which further lubricate interaction between constituent parts of a system. Each of these evolutionary technologies has ushered in refined architectures such as 2-tier, n-tier, and service-oriented architecture.
The industry, in building scalable systems that are also maintainable, has widely accepted as best practice to separate the data layer from the business layers. The user interface and business layers have evolved as well, making the logical separation of user interfaces and business layer physical with the advent of distributed component technologies. Even platform and vendor coupling has proven to be undesirable, and today components can communicate with one another using messaging standards sanctioned by non-proprietary standards bodies such as W3C and Oasis.
With business rules being such a critical part of any application architecture, there are likely great opportunities for decoupling business rules from the main application or process layer. Often, the idea with decoupling is that the less one component or service knows about the other, the more free each component or service is to change and evolve. The same is true
for business rules, and this is the heart of the case for using a rule-based engine.
Why Use a Rule-based Engine?
A rule-based engine isolates business rules from the application making the rules eminently reusable. Since business rules are (or should be) organizationally universal-as opposed to application specific-rule-based engines can provide unprecedented reuse (another goal of decoupling) of business rules. In addition, business rules that are visible to mere mortals (non-developers) are much better understood and this transparency introduces higher accountability within teams. In software development, a team is not just comprised of developers and architects. While these roles are very important, software product development includes stakeholders, line-of-business managers, business analysts, and quality assurance analysts to name just a few.
A recent report in CIO Magazine found that up to 70% of CIO budgets are spent on maintenance. Even conservatively, if you assume that half of the maintenance costs are attributed to business rules maintenance; it is likely that upward of 35% of IT budgets are spent managing business rules. A recent report by IDC supports these numbers. According to IDC, three-year net ROI for organizations deploying rule-based engines is in excess of 100% through 25%-80% reductions in development costs. The report goes on to cite increases in profitability and decision-making outcome of up to 50%.
The reason for the increase in productivity and ROI is simple. Separating business rules from programming code allows non-programmers to maintain business logic without writing code. Even for the most talented developers, writing code is expensive and error prone (if you have any doubts about this assertion, please see Test-Driven Development). The ability for interdependent team roles to have a hand in business rule management naturally reduces the time, risk, and effort inherent to programming changes to business rules in a vacuum. Shared business rule management leads to reduced development schedules and lower maintenance costs.
In addition, extracting business rules and making them available to the wider team promotes visibility and understanding of business policies and procedures, which serves to promote consistent decision making which leads to profitability. Fair Isaac, a leading rule-based engine vendor corroborates these assertions, reporting that in interviewing hundreds of customers throughout the world, "a 25% compression of development time is quite common, along with cost savings for developing new applications of up to 80% and maintenance of applications of up to 75%".
Another great benefit to isolating business rules is that it makes them eminently testable. This is a core value that any Test-Driven Developer holds dear, and a business rules engine can be thought of as a way to provide a different kind of dependency injection to your applications.
Introducing Microsoft Business Rules Framework
The Microsoft Business Rules Framework is a fully
functional rule framework originally intended for plugging in various rule executors and translators. The Microsoft Business Rules Engine is Microsoft's implementation of their own rule language and corresponding translator components as well as execution components based on the Rete algorithm (defined later) that plug in to the Microsoft Business Rules Framework and is commonly referred to as the Microsoft Business Rules Engine (BRE).
A rule-based engine isolates business rules from the application making the rules eminently reusable (and not to mention testable).
While the Microsoft Business Rules Framework and BRE are still relatively new (both were originally released with Microsoft BizTalk Server 2004), the Rete algorithm is not. The Artificial Intelligence Journal published a paper by Dr. Charles L. Forgy in 1982 entitled “Rete: A Fast Algorithm for the Many Pattern/Many Object Pattern Match Problem”. Dr. Forgy first published his research work on the Rete algorithm in 1974 as a an efficient pattern-matching algorithm which is capable of evaluating rules at a very high rate of speed with little regard for the number of rules being considered. The algorithm works by cross-checking a business fact with business policies to determine which rules should be considered for execution. If a rule does not need to be considered, it is skipped altogether. Also known as an inference-based rules engine, the Microsoft BRE also supports forward chaining of business rules which causes the BRE to re-evaluate rules in a policy when the action of a rule that has fired causes a change to the state of a fact that has otherwise already been asserted.
Although the Microsoft Business Rules Framework (and MS BRE) ships with Microsoft BizTalk Server 2004, 2006, and 2006 R2, this is where any association to BizTalk Server ends. Microsoft defines the Business Rules Engine as a stand-alone application that consists of a number of modules, support components, and tools. What you get out of the box is Microsoft's implementation of pluggable components which, in conjunction with the Business Rules Framework, is commonly referred to as Microsoft BRE. The primary modules include the Business Rules Composer for constructing policies, the Rules Engine Deployment Wizard for deploying policies created in the Business Rules Composer, and the Run-Time Rule Engine that executes policies on behalf of a host application. I will look at these modules in more detail as I apply a practical example of how to create a business rule within a policy, and call it from a .NET application.
What it Isn't
As I mentioned before, the MS BRE has nothing to do with BizTalk Server. It ships and is installed with BizTalk Server but you can take full advantage of MS BRE without BizTalk Server messaging or orchestration. This means that if you choose to only install MS BRE on a machine, you can do so with a very small footprint. However, keep in mind that since MS BRE is not a separate SKU, you can only get it with BizTalk Server, and as such, you must meet licensing requirements for BizTalk Server even if you only use MS BRE. While this may sound intimidating, doing any amount of market research on competing rule-based engines will quickly prove that the price point in which Microsoft places the Developer and Standard editions of BizTalk Server 2006 better makes MS BRE a compelling choice for bringing a fully
functional rules engine into the enterprise.
In addition, MS BRE has nothing to do with Windows Workflow Foundation (WF) Rules. While there are similarities, it is important to understand that MS BRE is a product that is developed, maintained, and supported by a different product team inside Microsoft. WF is not a product. WF is a framework for building workflow enabled applications and services. WF supports the execution of business rules; however, the features provided in WF Rules in the current shipping version of .NET 3.5 pale in comparison to MS BRE. WF Rules lacks a rule editor that can be used outside of the developer role and lacks a rule repository. Like most of the features in WF, this makes WF Rules a solid starting point for building out additional rule-based engine
functionality, but at its core merely provides an engine which is capable of executing rules. In fact, in early drops of WF, WF workflows were calling Microsoft BRE to demonstrate proof of concept scenarios before WF Rules were fully baked. While Microsoft has not taken a position on the future of each offering, I can only speculate that these competing offerings will converge, perhaps as part of the Oslo vision. Until then, I believe, and others agree (please see sidebar “Comparing Microsoft BRE and WF Rules”) that MS BRE is a stronger choice for integrating rule-based engines into your applications and services.
Microsoft Business Rules Framework Architecture
The architecture for MS BRE is comprised of design-time and run-time components. As shown in Figure 1, design-time components that include the Business Rules Composer are provided via a separate user interface (outside of Visual Studio) to manipulate the Vocabulary, Rule Store, and Rule Set
object model. While the BRE is a first-class citizen in BizTalk Orchestration, I will work exclusively with the Business Rules Composer and Visual Studio 2008 to develop, test, and execute the rule set in this article.
![Figure 1: The Microsoft Business Rules Framework consists of design-time and run-time components. Diagram courtesy of Microsoft Corporation.](https://codemag.com/Article/Image/0811071/Garibay Figure 1.jpg)
The Vocabulary Object Model allows developers and business analysts to use the Business Rules Composer to create domain-specific definitions for data, or facts that are represented in various states.
The Rule Set
object model allows for developers and analysts to build the rules that will consist of raw facts that can be either XML message-based, any .NET
object, or a field in a database table or in-memory dataset. Rules are grouped according to business domain and are logically organized as Policies. For example, you may have a rule that states that “If the customer is a preferred member, always apply a 10% discount on the total checkout price”. This rule might be just one rule in a rule set that is logically represented as the discount policy for the company.
Once vocabularies and policies/rule sets are created, they need to be persisted to the Rule Store, and the Business Rules Composer uses the Rule Store
object model to do so. By default, the Rule Store is a SQL Server database; however, it is possible to use a file or other backing store (with some elbow grease, of course). Using SQL Server as the default repository for policies and vocabularies has some obvious performance and management benefits. Rules and vocabularies are serialized to BRL (Business Rules Language), which as you might imagine is an XML representation of the policy and rules.
From a run-time perspective, an application, such as a Smart Client, Console, WCF Service, WF application, or BizTalk Orchestration, works with a Policy
class that provides the integration glue with the BRE. The Policy works with an instance of the Rule Engine
class, thus shielding the developer and application from intimate details about the BRE itself.
The Rule Engine
class is the workhorse behind the BRE and is responsible for the execution of the business rule policies. The Rule Engine
class takes a policy (rule set) name as an argument, along with corresponding facts and determines which rules are applicable given the facts, translates the rules from BRL to in-memory
object graphs, and executes the appropriate rules. I will cover this in more detail later, but it is important to understand that every rule has a condition, predicate, and action. This means that given our now canonical rule (“If the customer is a preferred member, always apply a 10% discount on the total checkout price”), if the rule executes, and the condition in the rule evaluates to true
, the 10% discount will be performed.
Finally, a Windows Service, known as the Rule Engine Update Service, monitors the Rule Store for changes to rules or policies. If there is a change, the Rule Engine Update Service updates the Rule Engine's local cache to ensure that Rule Engine instances that are bound to a live Policy instance are updated in real time, and to also ensure that any subsequent Policy invocations use an instance of the Rules Engine that is synchronized with the Rule Store.
The magic of the BRE is that it performs well because it is inference based. What this means is that the BRE will only consider rules that apply to a given fact. Instead of looping through dozens, hundreds, or thousands of rules, the BRE creates an agenda of rules to execute that are associated with the fact. This could be a single rule or several. Once the rules are added to the agenda, they are executed one by one until the execution cycle terminates. This means that you might have one or several corresponding actions resulting in a number of rules firing. I'll cover agenda and priority and provide an example of forward chaining towards the end of this article.
BRE Roles
As discussed, it takes much more than sheer programming to build a software product, and one of the main
objectives of the Microsoft Business Rules Framework is to help lubricate communication and collaboration between team members in various yet intersecting roles. By using the Business Rules Composer, all team roles can work together to implement the rules as part of a policy that makes sense in a business context and is verifiable and traceable by all. While I will not go on a rant about the merits of Domain Driven Design here, the power of sharing a common language and taxonomy with your entire team is a tremendous boost to productivity, comprehension, and morale.
Software product development teams also include stakeholders, line of business managers, business analysts, and quality assurance analysts to name just a few.
Once your rules and policies have been authored and tested, they can be deployed in a development environment by anyone with access to the Business Rules Composer. However, in Staging and Production environments it is likely that a release engineer or an administrative member of a deployment team will be responsible for pushing out new policies and updated existing policies by introducing a new policy version. This is precisely what the Deployment Utility is for.
Coming to Terms
I have already covered many of the following terms in this discussion, but let me provide some precise definitions by example for clarity sake. Hopefully, after reading this article, you will be well versed in the lingua franca that makes up the Microsoft Business Rules Framework.
Policy
A Policy is a versioned logical grouping of rules. It is represented by the Policy
class, which allows a calling component to execute a corresponding rule set that is bound to the policy. For example, a discount policy would consist of rules about how and when to apply discounts to purchase orders. You create a Policy using the Business Rules Composer, and execute a policy via the Policy
class. Policies are versioned, and once a version has been deployed, the policy is immutable. This ensures that a policy version remains sacrosanct, and also supports concurrent policy versions.
Rule
A rule is a statement that consists of a condition and actions. Rules are commonly created within the Business Rules Composer. A rule consists of a condition that evaluates facts and a corresponding action to take if the condition evaluates to true
. If the preferred member condition is met, then the corresponding action will be to apply a 10% discount to the purchase. It is possible to program directly with the Rule
class, but in this article you will build rules using the Business Rules Composer and execute rules by using the Policy
class.
Condition
A condition simply consists of predicates that apply to a fact, and always returns true
or false
. Examples of predicates are between, greater than, less than, equal to, etc.
Facts
Fundamentally, a fact is in-memory data that is acted upon within the BRE. For example, a Customer
object is a memory type that contains information about a customer. The information is represented as public properties, such as First Name, Last Name, and Preferred Member. These fields are referred to as “fact slots” which are then used in conjunction with a condition and action to form a rule. In the example, the “preferred member” fact would map to the Preferred Member fact slot on the Customer fact.
Fact Types
There are actually two types of facts. Short-term facts are passed into the Policy
object and removed from memory as soon as a policy completes execution. Short-term facts are introduced to the Policy
class as .NET
objects, XML document instances, or database rowsets. Long-term facts remain in memory across multiple execution cycles. An example of a short-term fact might be the Customer instance passed into the Policy instance, while a long-term fact might be the standard shipping rate to apply. Since the standard shipping rate seldom changes, this is an excellent candidate for a long-term fact, which is cached in memory to be reused throughout rule-engine execution cycles. In addition, long-term facts can be configured to refresh cache as necessary.
Actions
An action is the consequence of a rule being executed within a policy that yields a true
condition evaluation. The action results in a function call that is wired up within the Rules Engine Composer. For example, given a customer who is a preferred member, the action may be to set the Discount Percentage field on the Customer fact itself. This is just one of several possible actions.
Vocabulary
A vocabulary is simply a set of user friendly business definitions in the language of the domain that map to a fact or fact slot. For example, the preferred member status of a customer may be represented in SQL as "SELECT MembershipStatus from Customers WHERE LastName= “Deniro”. While this statement may be pretty straightforward, for non-developers, the T-SQL syntax may not be as intuitive. You can use a vocabulary to define the fact and give it a friendly name such as “MemberStatus”. Now developers and non-developers can have domain-specific conversations in building a business rule around pricing models for customers who are members.
Installing and Working with the Microsoft Business Rules Engine
Please don't let the fact Microsoft BRE ships with BizTalk Server intimidate you. While BizTalk Server is indeed a robust server platform that requires experience to install and configure, the installer for BizTalk Server 2006 and later has been significantly streamlined compared to previous versions, and installing the Business Rules Engine is very straightforward.
Up to 70% of CIO budgets are spent on maintenance.
In fact, as you'll learn below, you can install the BRE completely independently of other BizTalk components, making installation a breeze:
![Figure 2: To install Microsoft BRE, simply launch the BizTalk Server installer.](https://codemag.com/Article/Image/0811071/Garibay Figure 2.png)
![Figure 3: Installation of the Microsoft BRE and corresponding Business Rules Framework simply consists of selecting the Business Rules Components option.](https://codemag.com/Article/Image/0811071/Garibay Figure 3.png)
![Figure 4: The Microsoft Business Rules Framework is installed automatically by selecting the Business Rules Components. These are the only components required to begin using Microsoft BRE immediately; however, the product documentation is also recommended.](https://codemag.com/Article/Image/0811071/Garibay Figure 4.png)
![Figure 5: Following installation, it is necessary to configure the Microsoft BRE components.](https://codemag.com/Article/Image/0811071/Garibay Figure 5.png)
![Figure 6: Configuration is very simple and only requires a host name and account credentials for the Rule Engine Update Service.](https://codemag.com/Article/Image/0811071/Garibay Figure 6.png)
![Figure 7: The Summary lists the components that will be configured.](https://codemag.com/Article/Image/0811071/Garibay Figure 7.png)
![Figure 8: The Microsoft BRE is installed successfully in a matter of minutes.](https://codemag.com/Article/Image/0811071/Garibay Figure 8.png)
A gentle reminder: Please remember that although the Microsoft Business Rules Framework and BRE components are fully
functional in a standalone configuration, any machine running BRE, or any other BizTalk Server components must be fully licensed for BizTalk Server 2006 or better.
With the Microsoft Business Rules Framework, and Microsoft Business Rules Engine installed, you are ready to create your first policy!
Creating and Deploying a Policy with the Business Rules Composer
I am going to continue working with the same business rule I've used in previous discussions. Without knowing anything about the business domain, by simply reading the rule in plain English, it is evident that I am dealing with a customer and purchase domain.
According to IDC, three-year net ROI for organizations deploying rule-based engines is in excess of 100% through 25%-80% reductions in development costs.
Both of these entities are fundamental for composing the business rule and for BRE execution, because the rule must first determine if the customer is a preferred member, and if so, apply a 10% discount to the purchase order.
Figure 9 shows a model of the Customer and PurchaseOrder entities, and the code for each entity is provided in Listing 1 and Listing 2 respectively. Recall that the BRE can work with XML, a database or .NET
objects, and in this case, since I am modeling the domain by using C# classes as my domain entities, I will simply use these same
objects when working with the BRE.
![Figure 9: The Customer and Purchase Order business entities are used to model the business domain.](https://codemag.com/Article/Image/0811071/Garibay Figure 9.png)
The sample code that I've prepared for this article (please refer to sidebar “Solution Download” for download instructions) contains three projects:
Of course, when I try to compile my Acme.RetailOperations.Tests project, I get a bunch of compilation errors because the CustomerCheckOut
method doesn't exist yet.
Pop into the empty PurchaseService
class and write enough code to at least get the unit test to compile. Listing 4 shows the contents of my very primitive CustomerCheckout
method implementation, but remember, the goal at this point is to write just enough code to compile the unit test as shown below:
namespace Acme.RetailOperations
{
public class PurchaseService
{
public void CustomerCheckout(Customer customer)
{
// some logic
}
}
}
At this point, the unit test compiles but if you run it, it should fail because in setting up the Customer and PurchaseOrder within the unit test, I've initialized the DiscountPercentage to 0%. Despite the PreferredMember
property being set to true
, there is no business rule or policy for acting on this (the body of the CustomerCheckout
method is empty), and as expected, the test fails as shown in Figure 10.
![Figure 10: The unit test fails because the Microsoft BRE policy has not yet been integrated into the application layer.](https://codemag.com/Article/Image/0811071/Garibay Figure 10.png)
The goal now is to write enough code to get the test to pass. In order for the test to pass, you need to enforce the business rule, and you'll use the Microsoft BRE to do just that.
Start the Business Rules Composer by going to Start > Program > Microsoft BizTalk Server 2006 > Business Rule Composer as shown in Figure 11. In the upper left-hand corner, you will find the Policy Explorer. Right-click the Policy root and click “Add New Policy” as shown in Figure 12. Provide a name for the policy that is intuitive and representative of the business domain, such as “Customer Discounts Policy”. As shown in Figure 13, you will notice that the Policy has been automatically versioned to 1.0. Right-click the version, select “Add New Rule”, and name the rule “Preferred Member Customer Discount”.
![Figure 11: The Business Rules Composer is started from the Microsoft BizTalk Server 2006 program group.](https://codemag.com/Article/Image/0811071/Garibay Figure 11.png)
![Figure 12: Adding a new policy allows you to group business roles according to domain-specific groups.](https://codemag.com/Article/Image/0811071/Garibay Figure 12.png)
![Figure 13: Once a new policy is created, it is versioned and cannot be changed once deployed to the repository.](https://codemag.com/Article/Image/0811071/Garibay Figure 13.png)
On the right pane is a surface area on which to build your condition. As you might imagine, this is simply an “If” statement with some predicates. Select the “Equal” predicate as shown in Figure 14. You will use the equality predicate to determine if the customer is a preferred member.
![Figure 14: The equality predicate is just one of several predicates for building conditional business rules within the Business Rules Composer.](https://codemag.com/Article/Image/0811071/Garibay Figure 14.png)
To do so, you need to tell the BRE which fact slot will contain the data that determines if the customer is a member, so under Fact Explorer, click the .NET Class tab, right-click “.NET Assemblies”, and click Browse. As shown in Figure 15, a list of assemblies appears. The list is an enumeration of the assemblies in the Global Assembly Cache, which is a requirement for .NET
objects that will be used as facts (if you have not already done so, add the Acme.BusinessEntities.dll
assembly to the GAC prior to browsing for it). Select the Acme.BusinessEntities.dll and click OK.
![Figure 15: Assemblies containing facts to be asserted must be added to the Global Assembly Cache (GAC).](https://codemag.com/Article/Image/0811071/Garibay Figure 15.png)
Both Customer and PurchaseOrder
classes are enumerated under the “.NET Assemblies” root, and if you expand the Customer
class, you will find assessors for all public properties, including the PreferredMember
property as shown in Figure 16. Drag the PreferredMember get accessor to the Condition surface area and drop onto Argument 1. The condition should now look like Figure 17. Now, click “Argument 2” and type “true
” without the quotes as shown in Figure 18. You now have a full condition that inspects the PreferredMember fact slot on the Customer fact and tests the value for equality to true
. Recall that a condition will always result in a Boolean value.
![Figure 16: Facts consist of fact slots, which in the case of .NET types include assessors for working with the fact.](https://codemag.com/Article/Image/0811071/Garibay Figure 16.png)
![Figure 17: An equality predicate requires two arguments. The first argument is provided by dragging and dropping the corresponding fact slot from the Customer fact.](https://codemag.com/Article/Image/0811071/Garibay Figure 17.png)
![Figure 18: A condition is simply an If/Then statement that will always result in true
or false
.](https://codemag.com/Article/Image/0811071/Garibay Figure 18.png)
With the condition complete, all that is left is to provide the BRE with an action to execute when the condition is true
. In this case, you simply want to set the DiscountPercentage
property on the PurchaseOrder fact to 10%. Drag the DiscountPercentage set accessor from the PurchaseOrder fact and drop it on the Actions design surface. Figure 19 shows the compete rule with conditions and corresponding action that should fire if the condition evaluates to true
.
![Figure 19: A business rule is the combination of the condition and action should the condition evaluate to true
.](https://codemag.com/Article/Image/0811071/Garibay Figure 19.png)
Note that creating vocabulary definitions are useful for creating friendly names for otherwise esoteric facts and fact slots. For example, if you are using an XML message as a fact and have a complex XPath query to map a condition argument to a fact slot, you may quickly defeat the purpose of exposing your rules in a way that supports inter-role collaboration. The solution to this dilemma is to create vocabulary definitions that act as aliases for fact slots that would otherwise be unreadable to the non-developer (even I detest reading XPath statements!). Since you are using .NET types that are modeled after the business domain, I have skipped creating vocabulary definitions altogether.
If there are two or more rules that use the same fact slot in the condition, the BRE will determine execution order based on priority.
At this point, you have successfully created the “Preferred Member Customer Discount” rule within the “Customer Discounts Policy” policy. Right-click the policy and save the rule.
Recall that a policy can contain one or more rules, and as such the policy is the logical unit of deployment. Also recall that policies are versioned and once a policy has been deployed, it is immutable. Therefore, before you publish and deploy the policy, you need to test it.
Testing a Policy by Implementing the IFactCreator Interface
As I've discussed, at run time, each of these facts and fact slots will be asserted into the BRE and the BRE will determine which rule(s) to add to its agenda based on the presence of facts. For testing purposes, it is necessary to provide the Business Rules Composer with a hydrated instance of the Customer
object which it will use to determine which rules should be added to the agenda. To hydrate an instance, you must first create what is called a “Fact Creator”.
Dr. Forgy first published his research work on the Rete algorithm in 1972 as an efficient pattern-matching algorithm which is capable of evaluating rules at a very high rate of speed.
Creating a Fact Creator is extremely straightforward, and simply entails implementing the IFactCreator interface on a class to provide a surrogate Customer
class to the BRE using a prescribed interface.
The CustomerFactCreator
class, shown in Listing 5 shows the implementation of the IFactCreator. The CreateFacts
method fulfills a contract to return an array of objects. These objects are the facts that are used by the BRE to conduct a pre-deployment test. In this case, I have simply copied and pasted the Customer and PurchaseOrder initialization code from the unit test to the CreateFacts
method, created a single dimension array to hold the Customer instance, and fulfilled the contract by returning the array.
With the Acme.BusinessEntities.dll in the GAC, right-click “Version 1.0” of the Customer Discounts Policy, and then select “Test Policy” as shown in Figure 20. A screen appears which allows you to select the CustomerFactCreator. Click “Add”, select the Acme.BusinessEntities.dll from the list of .NET assemblies, and then click “OK”. The Business Rules Composer will query the assembly for any Fact Creators and enumerate them. As shown in Figure 21, select the CustomerFactCreator and click “OK”.
![Figure 20: The Business Rules Composer includes an integrated tool for testing condition behavior and agenda plan.](https://codemag.com/Article/Image/0811071/Garibay Figure 20.png)
![Figure 21: A Fact Creator must be supplied to the Business Rules Composer testing tool to assert the required facts for execution.](https://codemag.com/Article/Image/0811071/Garibay Figure 21.png)
Now click “Test”. Immediately, the agenda results are displayed in the test output pane as shown in Figure 22.
![Figure 22: Agenda results are output immediately following the assertion of test facts into the BRE.](https://codemag.com/Article/Image/0811071/Garibay Figure 22.png)
Understanding Agendas and Priority
Now I want to look at the test results of the Customer Discounts Policy test by reviewing the agenda results in Figure 22.
You used the CustomerFactCreator to assert the Customer instance as a fact to the BRE. The BRE then looked for any rules that contain conditions which map to fact slots that are present in the fact that was asserted. Because the Customer indeed contains a field which indicates if the sample customer is a preferred member, the rule was added to the agenda and executed. The condition evaluated to true
, because the PreferredMember fact slot on the Customer fact was set to true
in your CustomerFactCreator. Since the Customer was asserted as a short-term fact, and there are no other rules in the policy, the Customer instance is removed from memory and the BRE execution cycle terminates.
If there are two or more rules that use the same fact slot in a condition, the BRE will determine execution order based on priority. Priority is set on each rule within the Business Rule Composer. A rule with a higher priority fires first.
This has been a good test for testing that the condition of your rule is behaving as expected, and that the agenda is loading rules according to the expected priority, but you still don't know if the action is working as designed. There is no good way to do this within the Business Rules Composer.
Fortunately, you have a unit test in your Visual Studio solution that is up for the job!
Calling a Policy Programmatically
With the test complete, right-click Version 1.0 of the policy, click “Publish”, and then click “Deploy” to deploy the policy to the repository as shown in Figure 23. You will notice that you can no longer modify the policy by editing existing rules, removing rules, or adding new ones. Since the policy has been deployed, you cannot change it without creating a new version of the policy.
![Figure 23: Policies can be deployed to the repository directly from the Business Rules Composer or using the BRE Deployment Utility (not shown).](https://codemag.com/Article/Image/0811071/Garibay Figure 23.png)
Recall that although your ExecuteCustomerCheckOutDiscountAmountShouldBeTenPercent unit test compiles (Listing 3), the last time you exercised it, it failed. It failed because there is no implementation to the CustomerCheckOut
method skeleton and therefore, the DiscountPercentage is zero just as it was before the test ran.
The Rule
Set
object model allows for developers and analysts to build the rules that will consist of raw facts, which can be either XML message-based, any.NET
object, or a field in a database.
Jumping back into Visual Studio, in the Acme.RetailOperations project, right-click References and add a reference to the Microsoft.RulesEngine.dll which contains everything that you need to integrate the Microsoft BRE into your application. The Microsoft.RuleEngine.dll
assembly resides in the C:\Program Files``\Microsoft BizTalk
Server 2006\ folder as shown in Figure 24.
![Figure 24: Programming with the Microsoft BRE is as simple as adding a reference to the Microsoft.BusinessRules.dll
assembly.](https://codemag.com/Article/Image/0811071/Garibay Figure 24.png)
I want to focus on the enforcement of the business rule, so I am not going to spend any time on other implementation details. You certainly might expect the CustomerCheckOut
method to carry out additional chores such as checking inventory, calculating shipping charges, and authorizing a credit card.
With the reference to the Microsoft.RulesEngine.dll added, add the following using statement to the PurchaseService.cs
file:
using Microsoft.RuleEngine;
Listing 4 shows the contents of the PurchaseService
class, which will act as your primitive application layer.
You need to split the Customer and PurchaseOrder out into two separate facts because the BRE will work with each fact separately. You can accomplish this by simply creating an object array and adding the Customer instance and decomposed PurchaseOrder instance to the fact array.
With the facts ready, simply instantiate a Policy and provide the name of the policy you are working with in the constructor. Note that you can also provide a specific version of the policy, but if omitted, the newest policy will always be used. Now, call Execute on the Policy instance and pass in the facts array.
When the BRE is done executing the policy, it will set the DiscountPercentage
property on the instance of the Customer
class to 10% which the PurchaseService maintains a reference to. Now, re-run the ExecuteCustomerCheckOutDiscountAmountShouldBeTenPercent unit test and it should pass with flying colors as shown in Figure 25.
![Figure 25: Once the BRE policy is integrated into the application layer, unit tests are critical to ensuring corresponding actions behave as expected.](https://codemag.com/Article/Image/0811071/Garibay Figure 25.png)
While this is a very simple implementation of a purchasing application layer, the loose coupling between the application layer and the business rules is powerful indeed. Not only is there less code to write (you've harnessed the power of a fully featured rule-based engine with two lines of code), the discount policy is free to evolve with the needs of the business.
Suppose a decision was made to increase or decrease the discount amount for preferred members, or to eliminate the rule altogether? Affecting this change would simply be a matter of updating the Customer Discounts Policy within the Business Rules Composer, testing the condition, and deploying a new policy version, all without changing a single line of code!
Understanding Forward Chaining
I have (hopefully) kept things relatively simple, and by now you should have a good understanding of the Microsoft Business Rules Framework and BRE fundamentals. One topic I have not talked about in detail is forward chaining.
The Rule
Engine
class takes a policy (rule set) as an argument and determines which rules are applicable given the facts, translates the rules from BRL to in-memory
object graphs, and executes the appropriate rules.
Suppose your company introduced a new business rule that ensures that the combination of the preferred member discount and any current sale discounts do not cause the final price to fall below margin. This is important to ensure that the preferred membership program doesn't inadvertently cost the company money.
Such a business rule might be: “If the discount percentage on an order would cause the unit price to fall below margin, adjust the discount percentage accordingly.”
While it may sound complex at first, it is really very simple. The only time this rule needs to be considered is when a discount percentage is present. Non-preferred customers rarely have discounts unless they use a discount code.
The Customer Discounts Policy would prioritize the existing rules such that the 10% discount rule fires first, and upon the Discount Percentage fact slot being updated, the margin protection rule would be considered. If the customer receives no discount, the Discount Percentage remains null
; however, if the fact changes as a result of a preceding rule, new rules must be considered. This means that the BRE would complete two cycles. The second cycle would fire as a result of the Discount Percentage fact being changed and thus a forward chaining of execution.
Summary
I have provided an overview of rule-based engine technology and Microsoft's implementation within the Microsoft Business Rules Engine which is based on the Microsoft Business Rules Framework.
You took an emergent approach to building the customer check out
functionality in your sample application by using Visual Studio 2008 and test-first development. You worked with the Business Rules Composer to create a policy and a rule, and tested the policy before deploying it to the Rule Repository.
You then leveraged the .NET Microsoft BRE API to integrate the policy you created and verified the correct behavior by running your unit test within Visual Studio.
While one of the strengths of implementing a rule-based engine is the ease of working with and deploying rules, it is very important to understand that a rule-based engine such as Microsoft BRE does not eliminate the need for strong application lifecycle management policies. While team and organizational productivity will increase significantly by leveraging the BRE, the fact that business rules can be updated with ease can also be a dangerous thing.
Organizations that effectively leverage a rule-based engine like Microsoft BRE have likely evolved to a higher level of thinking that makes rule management service-oriented. The decoupling and isolation of rules and policies from constituent applications fixes many old problems, but also introduces new challenges around change management because a change in one rule can have widespread organizational impact depending on the number of applications that integrate with the given policy. For this reason, it is critical to maintain development, integration, and testing environments for integrating rule changes-nothing changes here.
As an added bonus, if you and your team practice test-driven development, regression testing a policy change should be as simple as running corresponding batteries of unit tests manually or in the next automated build. While not a silver bullet, this is definitely one step forward in increasing business and IT alignment because the rules are centralized, transparent, and support flexible applications that are not only ready for change, but built for it!
You can install the BRE completely independently of other BizTalk components, making installation a breeze!
One final word of advice: It is very important to understand that despite the compelling productivity improvements that deploying Microsoft BRE will bring, I do not recommend embarking on a project whose sole purpose is to integrate a rule-based engine within the enterprise. Such a project is doomed to fail because technology projects for technology's sake rarely provide convincing evidence that there is value in the undertaking in and of themselves. The key with any software or IT project is to focus on delivering business value. Services, SOA, and high-performing rule engines like Microsoft BRE are merely a vehicle for doing so. Don't confuse the two.
The CHAOS report (see sidebar) finds that 13% of features in an application are often used, and 7% of features are always used. My advice to you is to work with the business members of your team to identify the 30% sweet spot and start with a business process or business rule or two. By taking a middle-out approach, you are bound to quickly and easily find processes for which introducing this flexibility would add significant business value. Think big, start small, prove your business
case and the rest will follow.
Listing 1: The Customer Domain Entity.
using System;
using System.Text;
namespace Acme.BusinessEntities
{
/// <summary>
/// Encapsulates elements used to describe a customer.
/// </summary>
public class Customer
{
private string m_FirstName;
private string m_LastName;
private bool m_PreferredMember;
private PurchaseOrder m_Order;
public string FirstName
{
get { return m_FirstName; }
set { m_FirstName = value; }
}
public string LastName
{
get { return m_LastName; }
set { m_LastName = value; }
}
public bool PreferredMember
{
get { return m_PreferredMember; }
set { m_PreferredMember = value; }
}
public PurchaseOrder Order
{
get { return m_Order; }
set { m_Order = value; }
}
}
}
Listing 2: The Purchase Order Domain Entity.
using System;
using System.Text;
namespace Acme.BusinessEntities
{
/// <summary>
/// Encapsulates elements used to describe a purchase order.
/// </summary>
public class PurchaseOrder
{
private string m_PurchaseOrderId;
private int m_TotalItems;
private decimal m_TotalPurchaseAmount;
private float m_DiscountPercentage;
private decimal m_ShippingCost;
private decimal m_TotalIncludingShipping;
public string PurchaseOrderId
{
get { return m_PurchaseOrderId; }
set { m_PurchaseOrderId = value; }
}
public int TotalItems
{
get { return m_TotalItems; }
set { m_TotalItems = value; }
}
public decimal TotalPurchaseAmount
{
get { return m_TotalPurchaseAmount; }
set { m_TotalPurchaseAmount = value; }
}
public float DiscountPercentage
{
get { return m_DiscountPercentage; }
set { m_DiscountPercentage = value; }
}
public decimal ShippingCost
{
get { return m_ShippingCost; }
set { m_ShippingCost = value; }
}
public decimal TotalIncludingShipping
{
get { return m_TotalIncludingShipping; }
set { m_TotalIncludingShipping = value; }
}
}
}
Listing 3: The PurchaseServiceTest Unit Test Class.
using System;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Acme.BusinessEntities;
using Acme.RetailOperations;
namespace Acme.RetailOperations.Tests
{
[TestClass]
public class PurhaseService
{
public PurhaseService()
{
//
// TODO: Add constructor logic here
//
}
private TestContext testContextInstance;
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
[TestMethod]
public void ExecCustomerCheckOutDiscAmtShouldBeTenPercent()
{
Customer customer = new Customer();
customer.FirstName = "Robert";
customer.LastName = "Deniro";
customer.PreferredMember = true;
PurchaseOrder order = new PurchaseOrder();
order.DiscountPercentage = 0f;
customer.Order = order;
PurchaseService service = new PurchaseService();
service.CustomerCheckout(customer);
Assert.IsTrue(customer.Order.DiscountPercentage == .10f, "Expected .10 but was {0}",customer.Order.DiscountPercentage);
}
}
}
Listing 4: The PurchaseService Application Layer.
using System;
using System.Text;
using Acme.BusinessEntities;
using Microsoft.RuleEngine;
namespace Acme.RetailOperations
{
/// <summary>
/// Provides purchase ordering functionality and integrates with BRE.
/// </summary>
public class PurchaseService
{
public void CustomerCheckout(Customer customer)
{
object[] facts = new object[2];
PurchaseOrder order = customer.Order;
// some logic
// Determine any discounts
Policy policy = new Policy("Customer Discounts Policy");
policy.Execute(facts);
customer.Order = order;
// some additional logic
}
}
}
Listing 5: The CustomerFactCreator Class.
using System;
using System.Text;
using Microsoft.RuleEngine;
namespace Acme.BusinessEntities
{
/// <summary>
/// Implements the IFactCreator interface for providing .NET types to the BRE for testing purposes.
/// </summary>
public class CustomerFactCreator : IFactCreator
{
#region IFactCreator Members
public object[] CreateFacts(RuleSetInfo ruleSetInfo)
{
object[] facts = new object[1];
Customer customer = new Customer();
customer.FirstName = "Robert";
customer.LastName = "Deniro";
customer.PreferredMember = true;
PurchaseOrder order = new PurchaseOrder();
order.DiscountPercentage = 0f;
customer.Order = order;
facts[0] = customer;
return facts;
}
public Type[] GetFactTypes(RuleSetInfo ruleSetInfo)
{
throw new NotImplementedException();
}
#endregion
}
}