AnyWhichWay
Introducing RuleReactor

Home > RuleReactor March 13th, 2016, Updated March 21st, 2016; March 31st, 2016; April 4th, 2016; April 11th, 2016

RuleReactor is a a light weight, fast, expressive forward chaining business rule engine leveraging JavaScript internals, lazy cross-products, and Functions as objects rather than Rete algorithm approach used in OPS, Clips, Drools, and nools.js. It can be used on both the client and the server.

All rule conditions and actions are expressed as regular JavaScript functions so a JavaScript debugger can be fully utilized for debugging RuleReactor applications. At 45K (21K minified) vs 577K (227K minified) for Nools, a comparable speed for most applications, plus a low memory impact pattern and join processor, RuleReactor is perfect for memory constrained apps like those on mobile devices.

As of the writing of this article, RuleReactor is in an BETA state.

In this article we:

  1. explain the rationale behind RuleReactor
  2. provide some basic guidance on use
  3. cover its internal mechanisms

Here are two example rules:

var reactor = new RuleReactor();

reactor.createRule("Measles",0,{p: Patient},
	function(p1) {
		return p1.fever=="high" && p1.spots==true && p1.innoculated==false;
	},
	function(p1) {
		p1.diagnosis = new Diagnosis("measles","High temp, spots, and no innoculation for measles.");
	});
	
reactor.createRule("Penicillin",0,{p: Patient, d: Diagnosis},
	function(d) {
		return d.name=="measles";
	},
	function(p) {
		p.treatment = new Treatment("pennicillin");
	});

Why Another Rule Engine?

Existing JavaScript rule engines either depend on error prone string based rule expressions, complex JavaScript syntax, or custom languages with parsers that make the overall memory footprint of the core library large while also sometimes limiting the number of logical operators available for testing conditions. Additionally, they typically don't support the use of native JavaScript debuggers in a manner that clearly maps to the business logic of the underlying code. Hence, developers are either sadled with learning a new language, getting thoroughly familiar with library internals, or learning a whole new toolset.

RuleReactor addresses the above issues as follows:

  1. The full set of JavaScript logical operators are available, since rule conditions are just boolean functions.
  2. Rule conditions and actions are just functions and can be set as break points in a native debugger.
  3. The minified library is 11K, compared to 227K for the popular nools.js engine.

Using Rule Reactor

RuleReactor uses regular JavaScript classes for its object model. In fact, once a class instance is inserted into the memory of a RuleReactor, changes to the instance from outside the context of RuleReactor will cause rules to fire within the reactor.

Rule Format

You can see a simple example of rules and data at https://github.com/anywhichway/rule-reactor/blob/master/examples/patient.html

The functions used as conditions should have as their call signature variables named the same as properties in the rule domain, return a boolean value, and be side effect free. However, there is nothing that enforces the side effect free constraint.

RuleReactor does not currently support truth maintenance or fuzzy logic in its public release; however, we have implemented them.

With a LOT of work, it would be possible to build a Rete, PHREAK, or or TREAT based matcher behind RuleReactor and compile existing rules accordingly. However, initial performance tests vs nools show this may not be necessary.

Terminology

Under The Hood

Under the hood RuleReactor.prototype.createRule analyzes the condition function to determine what domains are associated with what function parameter keys and stores these as the range for the rule.

It also extends the prototype of all classes in its domain to include references to the rule.

When a class instance is added to a RuleReactor using .insert(instance,...) the property pointing to impacted rules inherited from the prototype is used to access the rules and do the following:

  1. Bind the instance to the rules impacted.
  2. Augment the instance properties using Object.defineProperty so that any changes to property values are relayed directly to the impacted rules.
  3. Iterate across each impacted rule's conditions computing identifying cross-products that match.

At runtime, every time a new instance is added or a property on a bound instance is changed impacted rule activations are removed from the agenda and the conditions of the rule associated with instance are re-evaluated with a new cross-product. If all conditions are satisfied, the rule is activated again and put back on the agenda.

So long as activations are on the agenda they will be processed, starting with the most recent and highest salience ones first. When an activation is processed, its rule is fired and it may result in changes to other objects such that some activations are removed from the agenda and others are added.

Performance

Currently the performance in Firefox, Chrome, and Edge varys a great deal. FireFox and Chrome vary with respect to which is fastest depending on the types of rules and volumes of data. Edge at times unable to resolve rules.

There is a performance testing example in the test directory in the GitHub repository. Speeds as fast at 120,000 rules per second have been recorded for two rules operating across 200 objects and thousands of joins have been recorded in Chrome.

Getting RuleReactor

RuleReactor is available at: https://github.com/anywhichway/rule-reactor/

It is also available via npm install rule-reactor.

Copyright 2016, AnyWhichWay