Nick Malik has published a thought-provoking article on rules engines recently. You can read it at http://blogs.msdn.com/nickmalik/archive/2007/03/06/perhaps-it-is-time-to-declare-victory-in-the-battle-of-rules-engines-vs-dependency-injection.aspx. He adopts a fairly negative attitude to rules engines in general, suggesting various reasons why he considers that they are not often useful. His preferred route is to use dependency injection patterns, implementing rule logic in code components that are then loaded in some dynamic or configurable fashion into consuming applications.
Some of Nick's insights are really good. For example, I like his comment that with workflow engines "what you are really encapsulating is not the data, or even the process, but the capability of executing the process properly". Yes indeed. In a whitepaper published earlier this week at http://www.solidsoft.com/WhitePaperDownload/DownloadFile.aspx?file=WP%20Rules%20Processing%20and%20BPM.pdf, I showed that executable rule sets could be used to implement activity state processes, but stated that a workflow or orchestration engine was often a better bet because these tools are generally built to handle a number of run-time and execution issues such as state persistence, long-lived processes, etc.
I have long since abandoned the idea that rules engines are always the best choice for executing every identified rule in a solution. The very idea betrays a hopelessly immature and incorrect understanding of what a 'rule' is. What is a rule? I can't provide an entirely satisfactory answer. Within an implemented system, a rule is a piece of logic that imposes some kind of constraint on that system. That, I freely admit, is a very open-ended definition! Rules are embodied in many, many different forms within modern software systems and applications. Using dependency injection often proves to be the best approach, and I, for one, implement this pattern again and again in custom code.
Does that mean that rules engines are useless? I think not. Nick has, in my opinion, made the mistake of adopting a polarised position based on a simplistic understanding of rule processing. This is ironic because his point about workflow components being regarded as a 'subclass of rules engines' point the way to a more rounded and balanced point of view. They are indeed, from one perspective, a 'subset of rules engines'. In fact, almost any computational component of a system could be thought of in the same way. A database system embodies a plethora of rules in the way it organises, stores and manages data. The presentation tier represents rules directly to end users, and enforces them in human-computer interactions. A firewall is built on the notion of rules. And so on, and so forth.
Are rules engines useful? Yes, absolutely, when applied intelligently to the real-world problems and scenarios they are best suited to handling. They are particularly useful when exploited as part of an overall rule management system approach that includes repository, composition, management and deployment tools. This is where the real value of many commercial rule engine products lies. The engine is just one of many components bound into an overall framework. That framework provides the necessary tools for an organisation to identify, capture and express policies in a declarative, business-friendly fashion, to encode those policies using a rules-orientated language and to provide an execution environment for those rules. It allows organisations to preserve the traceability between business rules and code artefacts, to validate rules for completeness and accuracy, to re-use rules across different platforms and tiers and to version rule sets orthogonally to other logic.
Nick states that a rule engine provides "the ability to pass values to the engine and have it calculate a result that can be understood by the caller". This description certainly implies a rather narrow role for engines because it appears to locate an important subset of application logic within the confines and constraints of the rule engine runtime environment. Data is handed off to an engine which implements rule logic and passes some data back.
In reality, rules engines are often far more powerful than Nick implies. They generally handle rules expressed as 'productions'. A production is a simple construct. It contains a list of conditions which can be evaluated against state ('facts') and a list of actions that the engine undertakes if all the conditions are 'true'. Actions are very often not constrained by the rule engine technology. For example, many (most?) modern engines are very happy to execute 'action' functionality implemented in custom code components (Java or .NET objects, for example). The decision about which components are executed is controlled by the action lists in individual rule definitions which are declarative and can be changed easily. These rules engines are, therefore, excellent at implementing the very patterns that Nick advocates as an alternative! Modern rules engines are ideal for driving dependency injection via policies. When Nick states that dependency injection has 'won the battle' he entirely misses the point. Rules engines provide a very powerful way to implement and manage dependency injection while aligning the rule-based executable logic, including the logic encapsulated in custom code, with business rules and other types of policy.
I could, of course, be accused of misrepresenting Nick’s argument here. When he compares rules engines with dependency injection, he is not talking about dependency injection is a general sense, but specifically as a pattern for encapsulating rules. When using a rules engine, ‘injected’ custom action code represents activities, rather than evaluated conditions. Actions are embedded within the rules. Note, however, that actions are not separate to rules. They are a fundamental part of the rule definition in which they are embedded.
An equally important feature of some modern engines is their ability to interact with state directly within systems. While it is true that 'facts' are often asserted to an engine by an external system, some engines offer rich facilities for encapsulating these assertions within integration layers. For example, they may be able to connect directly to sources such as database tables or exploit custom data retrieval code via pre-defined interfaces. Some engines can invoke custom code components directly in order to obtain data or implement predicate logic. These engines are not an alternative to implementing logic in custom code. They are a means of driving custom logic and exploiting data directly within any tier of an application.
The ability to ‘inject’ custom code into the conditional logic on the left hand side of declarative rules serves to extend the usefulness of rules engines in implementing dependency injection patterns. Nick’s ‘battle’ therefore becomes non-existent. Rules engines have the benefit over custom code of enforcing a cleaner distinction between abstract rule definitions, which are often directly traceable back to business policy statements, and concrete logic implementations. This, in turn, makes it easier to manage and version rules over time, and to validate those rules. Rules engines can therefore significantly strengthen the basic dependency injection pattern for rules processing in contrast to the kind of custom code framework described by Nick.
Nick states that in rules engines, "the [rule logic] algorithm sometimes has to be encoded in a programming language that is executed as script. This is slow and inefficient". Well, yes, maybe. However, many (most?) engines do not use interpreted script in any way. Over the years, some of the fastest rules engines have consumed executable rule sets that are pre-compiled directly to machine code. In the .NET world, we have begun to see engines that exploit CodeDOM in order to generate JiTtable code dynamically at run time, and the introduction of lambda expressions and monadic patterns will open up additional avenues for implementing very efficient rule engine processing. Engines that implement the Rete algorithm represent rule sets as run-time node graphs. Data is passed from node to node using memory tokens. Evaluation code may possibly be script-based in some cases, but all the Rete engines I know use compiled predicate code, reflection mechanisms and the like. More importantly, a Rete engine can perform certain types of rule processing more efficiently than any custom code a developer is likely to create. They are especially efficient when performing forward chaining over data joins, and when handling large rule sets. It is entirely mistaken to assume naively that rule engines are inefficient compared to custom code. The true situation is far more complex and subtle.
Rules engines have an important role to play as one of many different approaches to implementing rule logic within modern systems. If they are under-exploited, I suggest that this is, in large part, because of a general ignorance about the state and nature of modern rule processing toolsets. The correct remedy to this is for architects and developers to learn more about what rules engines offer, and what they are capable of, so that they can make good decisions about how best to apply them.