Using Active Record As A Rule Engine
I posted a riddle about how to use Active Record as a Rule Engine, here is the answer.
First, just to remind you of the scenario:
Okay, so we have a database and we have a lot of rules. Let us consider first what a rule is, shall we?
A rule is usually composed of two distinct parts:
- What this rule applies to?
- What this rule should do?
For instnace, consider this rule:
- What this rule applies to: Employee with over 5 years senority.
- What this rule should do: Increase salary by 10%
Do you see where I am heading? Let us first look at the object model first:
I have got a Rule<T> class, which manages the rules. It has either an HQL Query which select the objects to works on, or a set of predicates that it uses.
The predicates are very thin wrappers over NHibernate.Expression namespace, and they mainly provide persistance support. The reason that I am using both is that is it very easy to provide a nice drag & drop interface for the predicates, which allow a business analyst to define the set of objects that this rule will run on. The HQL Query is for the more complex issues, where a developer will be needed.
I hope you noticed the IRuleAction<T> interface. This allows to write actions that accept an object, and do something to it. This can be something like Increase Salary, Fire Employee, etc.
Note that both the Rule<T> and the IRuleAction<T> has a collection of parameters. Those are strongly typed parameters. Which means that I don't deal with hash tables, or things like that. I get a strongly typed object that I can use in compile time.
For reference, here is the table structure that backs this up:
How this is working? Check out the Rule<T>.Execute() method:
public void Execute()
{
using (new SessionScope())
{
T[] items = GetItemsToWorkOnFromQueryOrPredicates();
foreach (IRuleAction<T> action in Actions)
{
foreach (T item in items)
{
action.Act(item);
}
}
}// all changes to the items are persisted here.
}
And here is how an action looks like (I omitted the uninteresting properties):
public class IncreaseEmployeeSalaryAction : IRuleAction<Employee>
{
public void Act(Employee item)
{
item.Salary.IncreaseBy(Parameter.Amount);
}
}
NHibernate can handle generic types quite well (but not generic type inferencing, thanks goodness), but it require the full generic type name, which can be a pain to write. Active Record does even that for me, so it is very easy to work with the system.
Hope you like the idea. An additional extention of this can be further processing of complex business rules that cannot be expressed in an HQL Query alone.
Update: Just to clarify, NHibernate is able to tell based on the XxxType column in each table which object it is, and it construct the object graph accordingly.
Comments
Comment preview