Best Practices
Playing With(in) the Rules
The Rule Manager Façade
Aug. 9, 2005 04:00 PM
A Sample RuleManager Component
Due to publishing constraints I've simplified the sample RuleManager code quite considerably for this article. For example, the sample RuleManager doesn't provide any features for dynamically loading or unloading different criteria types or groups of criteria types - instead it merely examines a directory for CFCs and assumes each is a criteria type CFC. I've also eliminated debugging features, a considerable amount of i18n functionality for multilingual applications (which is no small challenge to implement with a RuleManager), simplified the provided criteria CFCs, and omitted nearly a dozen common criteria types. One feature I'm particularly sorry to omit is the ability for individual criteria CFCs to introspect the provided rule context to determine their own applicability to the context. Although I feel this is an important feature for the sake of encapsulation and extensibility, it would simply require too much space to include in this article.
Having trimmed the code for the RuleManager sample to a bare minimum, here are the absolutely necessary features of the façade (RuleManager.cfc) for this application to function:
- Rule CRUD: Create/Read/Update/Delete methods for individual rules must modify the loaded ruleset XML (stored in memory as a property of the RuleManager.cfc object) and return or provide a means of accessing the text-representation of the current ruleset document for storage (which can be stored either in a file or in a database). Methods: getRuleNode, setRuleNode, ruleExists, deleteRule.
- Criteria CRUD: Create/Read/Update/Delete methods for individual rule criteria. Although rules must be identified with a unique id such as a UUID, a criteria node within a rule may be identified by its position within the rule so long as the rule identifier is used when fetching the criteria node. Criteria create/update/delete methods must also return the text representation of the XML ruleset or provide a means of accessing its text-representation for storage (see above). Methods: getCriteriaNode, setCriteriaNode, getCriteriaForm, getCriteriaXML, getRuleCriteria, deleteCriteria.
- Criteria Object Management: In order for the RuleManager to perform efficiently it must cache criteria-type CFCs in memory and use the cached CFCs for managing and interpreting individual criteria nodes. The façade (RuleManager.cfc) must also provide a list of the available criteria types. Methods: getCriteriaObject, getCriteriaQuery.
- Test Rule: This is the singular feature for which all other features exist. Once a user has created their rules and assigned their criteria your application must have a means of identifying and testing the applicability of those rules to apply the various application features you want controlled by user-defined rules. Method: ruleApplies.
In addition to these features of the façade, a common interface is required for the different criteria-type CFCs, which will be managed by the façade. This is accomplished by designating a predefined set of methods (including arguments and return types) that all criteria-type CFCs must share. With these methods in place the façade can then reference any individual criteria-type without needing to know any of the internal mechanics of the criteria.
At this point you might notice that almost all of the criteria-type interface methods include an argument named "context." If you remember from the previous sections of the article, one of the assumptions in the RuleManager application is "All parameters used by criteria-type CFCs in the evaluation of a rule must be provided in the criteria XML node or by an external context (CFC)." By providing the context in the getForm method the criteria-type object is able to determine what properties will be available for testing later. These values are supplied to the getXMLNode method through the form-scope and so are not needed in this method. The context is necessary however when describing the criteria (just as the names of context-properties are provided to the getForm method) and to fetch the current value of selected context-properties when evaluating the applicability of individual criteria when testing the rule.
About Isaac DealeyIsaac Dealey has worked with ColdFusion since 1997 (version 3) for clients from small businesses to large enterprises, including MCI and AT&T Wireless. He evangelizes ColdFusion as a volunteer member of Team Macromedia, is working toward becoming a technical instructor, and is available for speaking engagements.