Ayende @ Rahien

Unnatural acts on source code

A rule engine in less than 70 lines of code

As I continue to explore what we can do with DSL, I am getting more and more excited. Let us take a look at the following syntax:

when User.IsPreferred and Order.TotalCost > 1000:
    addDiscountPrecentage  5
    applyFreeShipping
when not User.IsPreferred and Order.TotalCost > 1000:
    suggestUpgradeToPreferred 
    applyFreeShipping
when User.IsNotPreferred and Order.TotalCost > 500:
    applyFreeShipping

The backend for that is a simple 68 lines class. Again, we had to extend the language to support the when keyword, but that is all we really had to do.

image

I tried to build the same using Windows Workflow, but I gave up after a few minutes. It was too much clicking, and not enough results.

It should look something like the image to the left, I assume.

From the perspective of maintainability and actually being able to look at what is going on, I know what I would like to have.

At any rate, you can get the backend here, and get the unit tests here. Both are part of the test suites of the Rhino DSL project.

To be frank, I can't believe how easy this stuff is.

 

.

Comments

Scott Allen
12/11/2007 02:15 AM by
Scott Allen

Try the PolicyActivity instead. You could put all those rules into a single ruleset for the PolicyActivity to evaluate and it will take care of managing priorities, and forward chaining on any intra-rule dependencies. There are other pros (and cons), but its a better fit than a sequential workflow (you don't want to control the flow of execution, you want the execution to evaluate declarative knowledge).

Tomas Restrepo
12/11/2007 02:30 AM by
Tomas Restrepo

Yep, Scott is right on the money; this kind of thing would be far more suitable to a policy, and actually chaining can make it easier to define them. For example, notice that your example actually has a single rule defined three times, whereas if you had chaining you could have it a single time.

BTW, you can actually invoke the WF rules engine outside of WF, so I guess that might be useful on it's own (though I haven't had a chance to use it like this yet).

Ayende Rahien
12/11/2007 02:40 AM by
Ayende Rahien

Tomas,

Can you explain why I have a single rule defined three times? I see only a single rule.

Tomas Restrepo
12/11/2007 03:23 AM by
Tomas Restrepo

Sorry, I wasn't very clear. I was probably presuming much from your small code snippet, so don't pay much attention to me about that. (I was thinking along the lines that the way your example calculates the "freeShipping" option didn't make much sense).

Chaining is more useful in slightly more complex scenarios were the rule applications have side-effects that can invalidate other rules in the policy (or make valid previously invalid rules).... this is useful, but can also be a performance drag (or introduce unwanted cycles). In most rules-based systems like that you don't really apply rules, but entire policies (you probably know that already), which, in your case, I'd guess would be a complete .boo file.

Ayende Rahien
12/11/2007 03:41 AM by
Ayende Rahien

The logic in the script is that the first one to match is the one that is being evaluated.

I am using boo file per policy here, yes.

Another option would be to define:

rule 'name':

when ...

Joe Ocampo
12/11/2007 04:55 AM by
Joe Ocampo

This is really impressive. I like how you added the "When" keyword and the then statement is inferred by statement block.

I am going to have to check out this code. Nice job.

Kris
12/11/2007 05:54 AM by
Kris

This looks really cool. It would be great to see a Hibernating Rhinos episode one day. BTW, thanks for the excellent video casts, it's been great to watch these as they are very informative and interesting. Your Resharper chops are truly impressive! I lost some of my skills after working in Eclipse all of last year!

Comments have been closed on this topic.