Rule Engine Part 3— Semantic & Attribute

Yini Yin
5 min readFeb 2, 2021

If you read Rule Engine Part I & II, you will see this evolution of Rule Engine, from good old Object Oriented technique such as inheritance, internal state, condensation of behaviours toward a single unit of operation, etc., to a more functional approach where a single method is used to operate on the essence of a specific business rule, it hugely reduces the amount boilerplate that comes with OO while keeping side-effect at bay. While functional style makes Rule Engine short and sweet, it also strips away one critical utility that OO offers — encapsulation of internal state and behaviours.

Take this rule below as an example:

The main task here is to determine whether a customer has already received a decision on a loan, if she has, she will not be allowed to re-apply for another loan for 30 days. There are two operations: first check if there is a decision made. Secondly, if there is, then we check if it has been 30 compares to today’s date. We have two methods: IsInStepdown and GetRemainingDays. As you can see GetRemainingDays handles the gnarly detail of getting days difference, and employing a tertiary operation to calculate how far we are into this 30 days period. Whereas IsInStepdown is responsible for conveying the flow of operation at a higher level. This rule also exposes a static field Duration because other parts of system need to refer to it. As we can clearly see splitting operations into small dedicated methods and shared state makes our code much more readable when it comes to complex business rules.

If we find ourselves needing to have a rule that spans across a great number of methods, we then probably need to ask if we are doing too much in a single rule, maybe we are covering more than one rule. In this case, it is better to break the rule into multiple rules.

This business rule can represented in a reasonably tidy manner in the realm of OO. Let’s see how do we do it in functional style Rule Engine:

Well, okay, I might have intentionally added in more rules to make it seem untidy as compared to previous class type example. But anyone who has worked on a financial system will know how complex it can get as regards to lending criteria!

In functional style rules, in order for related methods to share state we have to declare state at the top as either a field or a property. This tends to muddy the boundary of which state is shared by which methods. To reduce readability even further, helpers methods are all mingled into methods that are invoked by Rule Engine. And again we can’t easily tell which helpers methods are helping what.

While encapsulation gives us the ability to organise our business rule into small single purpose methods and coordinate them by private state should our business rule evolve into complexity over time, yet the amount of boilerplate we have to endure and repeat, hardly seems like a good deal especially we only bet on future proofing. For simple business rules that can be done by one or two lines of functional style code, this can be an overwhelming haze over business logics.

Time and time again, life has taught us we can’t have our cake and eat it. Or can we? Wouldn’t be nice if we could have encapsulation and clear boundaries of responsibility, yet, without the boilerplate of inheriting Rule Engine and Rule class. Wouldn’t be nice if we could run our business rules as class, but also as a method if we choose to. The answer is Attribute.

One important element of attribute is associating semantic meaning to object, similar to attaching a tag which others are looking for. When another program or application sees the name tag, it infers certain things and may change its behaviour accordingly. So, what if we tag classes that identified as rules, and tag its methods as validation methods, then let Rule Engine look for these tags and run them accordingly?

Say we have a couple of credit card loan lending rules to enforce: customer must be at least 18, and customer must live in current address for at least one year. Let’s decorate them as follow: we want to decorate a business rule class with RuleAttribute, and associate rules that are related into a category by RuleAttribute’s property Category. Inside a rule class, we want to decorate methods identified as validation methods with RunAttribute.

Notice the lack of boilerplate as our business rule classes are not forced to inherit from Rule Engine, and yet, we are able to preserve all the good things that come with class encapsulation.

And let’s define our attributes:

Simple enough! But then it comes the meat: how do we make these tags available to our Rule Engine so that it can infer their semantic and act accordingly? The answer is Reflection.

We search the whole code base for any types that are decorated with the RuleAttribute and associated with a specific category, then loop through them. For each rule type decorated with RuleAttribute, we get all its methods that are decorated with the RunAttribute, we then invoke them one by one, if any one returns false, we short circuit and mark this rule failed. If any one of the rules fails, we mark the rule category failed.

Let’s give it a test:

That’s all good. What if our validation methods are async?

Let’s define an RunAsyncAttribute and implement an async validator in Rule Engine:

The tricky part is when the validation methods in rule class are async, when we invoke them, we need to cast the returned type to a Task and await for it — line 24 & 26.

Also the reason we use foreach not Linq, is because methods in Linq such as All and Foreah, they don’t await each Task in the loop and short circuit when a Task dissatisfies its predicate. Whereas good old foreach awaits each Task and gives us the ability to continue or break.

Let’s test it:

One thing I have not elaborated, is how do we find all the classes that are decorated with Rule Engine tags. I factor it into a util method that takes attribute as a type parameter, scans through the whole assembly to search for types that have attribute of interest applied upon, as well as its category matches to the one we specified.

It has been quite a journey from class inheritance type Rule Engine, to now Attribute where we decorate rules to be run. I would say Attribute is the most powerful as it provides the benefits from both OO and functional style Rule Engine, yet the least intrusive. There is so much more we can extend upon, such as logging, error handling, with this concept of declarative programming. Maybe we could take it to the next level as to Aspect Oriented Programming, similar to libraries like postSharp and Fody. Or may we don’t have to limit ourselves to just business rule validations, we could well be using it for implementing patterns such as Pipes and Filters.

Sorry it has been a long read, I hope you get something useful out of. Please leave a comment if you feel like to share your experience as I am always keen to learn how would you apply Rule Engine to your projects/products. Full source code can be found here. PRs are always welcome!

--

--

Yini Yin

Try not to be a jack of all trades always end up being one. Dev@Barin