Business Rules: A Developer’s Perspective
Most of us know what a business rule is. But why is there so much brouhaha about it? Myriad rules of science and mathematics govern nature. And those rules continue to decide what we can or cannot do, humankind's supremacy over nature notwithstanding.
A couple of striking aspects of business rules is that they are man-made, often arbitrary, and mostly non-intuitive. A few classic examples to support the point:
1. An American company cannot sell certain goods to Iran. But for the latter's neighbour Iraq those restrictions do not apply.
2. Add 25% additional lead time to a future purchase order if the consignment is to be transported by road and the delivery date falls in the rainy season.
3. One can book sales orders and make reservations for goods that do not exist in inventory if it is for a platinum customer.
We, nerds, find those to be weird, even though the brick and mortar world finds nothing amiss there. It is not that we do not follow the rules while we code.
· We do not use variables before initializing them.
· We take care of the intrinsic difference between a NULL and an empty string.
· We do not treat phone numbers as numbers but string.
Yes, those also look strange to a "normal" (read, not a nerd) guy.
Despite our best efforts, those two viewpoints have remained as orthogonal as ever. Some utopians had dreamt about a technology scape dominated by power users and techno-functional consultants. The dream was not wholly irrational. But the general populace attached to business systems failed to evolve. And hence, the logical divide remains.
We techies generally do not want to lose a battle of wits quickly. Our science teaches us to resort to abstraction whenever we do not understand something. Abstraction hides many details and, yes, oversimplifies things. But given the highly deterministic nature of modern computing, we do not have too many choices either.
So, where do we start? The three "business rules" we came across a few paragraphs ago would be a good starting point. Let us quickly symbolize them as BR1, BR2 and BR3 respectively.
Is BRX applicable to all the use cases indiscriminately? Even though the rules appear to be arbitrary, their applicability is, surprisingly, highly specific. To state it formally for every business rule BRi it is possible to find a set of filter conditions {FCi1, FCi2,…FCin} which when applied to the set {U} of all the use cases yield a much smaller set denoted by {UCia, UCib,…UCim}.
What do we do after "filtering"? In the real world, action follows detection. After finding that something is out of the way you just do not sit on it; you do something about it. Right? Before we are presented with a list of things to be performed, let's quickly abstract the whole thing by stating that we are required to take a few actions denoted by the list [ACia, ACib,…ACip]. Please note that the actions form a list, not a set. Ordering is important when you perform a group of tasks.
Using abstraction, we have managed to express everything in terms of symbols. Now we need to convert symbols into things that a computer understands. By the way, a computer does not understand too many things. So, we must restrict ourselves, more or less, to variables, objects, functions, and collections.
A large object may represent each use case "U", instantiated from a class, say, Transaction. Shove all data related to a use case into the transaction object. Let us formally denote the two by TC and TO respectively; "TO" is an instance of "TC".
Each filter FC may be replaced by a function that accepts a transaction object as an input parameter and returns either True or False. True stands for selection and False stands for rejection by that filter. The formal representation of a function is Boolean Fn(trans_obj) when "n" stands for an integer in range i…j.
Each action AC is, of course, a function. That is the most intuitive of all. Let us formalize it this way: TC Fm(trans_obj), where m stands for an integer in range j+1…k. Please note that each function returns the transaction object, albeit the modified one, itself. That way, we can daisy-chain multiple functions like this: F2(F34(F23(TO3))) and still end up getting the transaction object as the return value.
A programming language where functions are first-class citizens like variables and methods, one may even consider maintaining a "function queue" as a list within TC. The controller program just needs to pass the function names (F2, F34 and F23) through the add method of the list.