With .NET being the next new thing, a lot of companies have been taking the opportunity to revamp and overhaul their existing applications. Today I'll tell a small part of the story of one such organization (a large retail company) who decided to redesign a large portion of their COM-based order-fulfillment system that had been built using the last new thing, Windows DNA.
One of the lessons learned from the years spent building the existing applications was that complex systems with a lot of business rules require a lot of programming code. Unfortunately, the takeaway from that lesson was not that it was essential to organize such code so that it's easier to maintain, but that they should instead invent some way to write a lot of code without hard-coding their code. This is where the Enterprise Rules Engine came in.
The idea was that business rules, such as the conditions required to cancel an order, do not need to be coded in things like C#. They could be placed in some external store, such as an XML file or a database table, and a programmer would never need to modify the actual "code" to change the rule. He would, instead, modify the XML file or database table.
Unfortunately, such wonderful ideas often come face-to-face with the ultimate idea-wrecker we call "reality." As it turns out, the ultimate rules engine that never involves modifying the core codebase looks something like "Microsoft Access" and many shops that try to build it end up with some horrible system that's halfway between that and a maintainable application. Today's example, featuring the Enterprise Rules Engine, is not much different.
Below is how one the rule to determine whether or not an order can be cancelled is coded. I'm not sure what I find the most interesting: that the comments contain the actual C# code needed, that this creates potentially the worlds largest constructor, or the fact that this actually manages to work with a system that can only compare strings. Fortunately, .NET 2.0 could present the opportunity to yet again rewrite the system ...
///////////////////////////// // CanCancelOrder ///////////////////////////// // Determines whether or not an order is eligible // for being canceled: // ( // ( order.OrderStatus != OrderStatus.Approved // && order.OrderStatus != OrderStatus.Packed // && order.OrderStatus != OrderStatus.Billed // && order.OrderStatus != OrderStatus.Shipped // && order.OrderStatus != OrderStatus.Canceled // ) // || ( IsAuthorizedForOperation ( // currentUser, // Operations.LateStatusCancel ) ) // ) ///////////////////////////// _ruleset.AddRule ( new EreRule ( OrderRule.CanCancelOrder, // -- Order Can Be Canceled When -- // new EreRuleAtomGroup[] { // -- Status Is OK -- // new EreRuleAtomGroup[] { // ( order.OrderStatus != OrderStatus.Approved new EreRuleAtomGroup ( new EreRuleAtom[] { new EreRuleAtom ( order.OrderStatus, Operands.IsNotEqualTo, OrderStatus.Approved ) } ), ... // && order.OrderStatus != OrderStatus.Canceled new EreRuleAtomGroup ( EreRuleAtomGroupType.And, new EreRuleAtom[] { new EreRuleAtom ( order.OrderStatus, Operands.IsNotEqualTo, OrderStatus.Approved ) } ) }, // -- User Is Authorized -- // new EreRuleAtomGroup[] { // || ( IsAuthorizedForOperation ( // currentUser, // Operations.LateStatusCancel ) ) new EreRuleAtomGroup ( EreRuleAtomGroupType.Or, new EreRuleAtom[] { new EreRuleAtom ( IsAuthorizedForOperation( currentUser, Operations.LateStatusCancel), Operands.IsEqualTo, true ) } ) } } ) )
And to check if an order can be canceled, simply, write IsRuleValid(OrderRule.CanCancelOrder) ...