
Using the Specification Pattern to Build a Data-Driven Rules Engine - jonblankenship
https://blog.jonblankenship.com/2019/10/04/using-the-specification-pattern-to-build-a-data-driven-rules-engine/
======
openasocket
There's a lot of interesting things you can build on top of these sorts of
rules engines, like optimizations. I had to deal with a system that allowed
users to enter arbitrary boolean expressions and we had to use them to filter
out records from a very large stream of data, and optimizing for speed was
very important. Thee's the obvious changes, like flattening out deeply-nested
expressions as much as possible. You can sort sub-clauses in an
AndSpecification so that the ones most likely to be false are first, so that
you are more likely to short-circuit and cut down on evaluation time (and you
sort in the reverse order for the OrSpecification). But you can actually do
some more advanced optimizations. In my case, we knew that for a given
expression provided by users the overwhelming majority of records would not
match. So for a given expression A we would derive a simpler formula B such
that A => B (and thus not B => not A) where B was cheaper to evaluate, then we
would evaluate the expression as B and A. If B was false it means A would be
false anyway, so we could short-circuit and skip the more expensive test.

~~~
PaulHoule
If you look close at that article, what they describe is not a real "rules
engine" but just a set of boolean valued functions.

Real business rules engines use something like

[https://en.wikipedia.org/wiki/Rete_algorithm](https://en.wikipedia.org/wiki/Rete_algorithm)

Production rules systems in the 1970s (when they were in vogue for "expert
systems") did not use indexes and generally had poor execution performance
compared to modern "business rules" engine.

Production rules are related to other logic-based methods of programming such
as "logic programming" (which tends to progress backwards from a desired
consequence to prove it is satisfied) or "theorem provers" that could
demonstrate that a section of code satisfies certain invariants.

------
layoutIfNeeded
In other words: an Enterprise Rules Engine
[https://thedailywtf.com/articles/The_Enterprise_Rules_Engine](https://thedailywtf.com/articles/The_Enterprise_Rules_Engine)

If you see this pattern at work, just run!

------
adamfeldman
In Clojure-land, there are a few similar projects. I enjoy working with Clara:
[http://www.clara-rules.org](http://www.clara-rules.org),
[https://github.com/cerner/clara-rules](https://github.com/cerner/clara-rules)

------
xsmasher
As soon as I saw "public class PriceSpecification" I got a creepy feeling.

Why shouldn't the specifications be more purely data, like {field:"price",
operator:">", level: 150 }

Then you DON'T need to write new classes for DividendYieldSpecification and
PeRatioSpecification - you just need to make sure those fields are available
in the data, or write support to derive them when they are requested.

~~~
wpietri
Things would work both ways, of course. For me the difference is a) the extent
to which I want to make the domain model explicit, and b) how much I value
build-time elimination of bugs vs run-time flexibility.

Data-driven approaches leave a lot implicit, meaning that it can be hard to
answer the question, "What's really possible here?" And they can only be
verified to the extent you have all the data, which usually means a lot of
checks are deferred to run time.

------
hummo56
So happy I did not have to work with Java for a long time. The amount of
boilerplate and cognitive overhead is insane! The factory was really the
cherry on the cake...

------
kkdaemas
Any other ML programmers look at this and see a very verbose discriminated
union?

------
BoiledCabbage
So many simple good ideas seem to come from Domain Driven Design.

~~~
pacala
Feeling cranky today, lockdown and everything. This is an elementary
interpreter, clumsily implemented because of all the object oriented cruft.

~~~
agentultra
Indeed... in Haskell this can be implemented with a GADT and an evaluator
which is really easy to express.

Still useful to know the "pattern" regardless of OOP cruft, Javascript
prototypes, or Haskell data types.

------
NicoJuicy
Important to mention, but when I checked out the specification pattern. It's
useful to know that it can only be applied if your programming language of
choice supports expression trees ( eg. C# )

~~~
emidln
What is an expression tree? I've done this with arrays of structs in C as well
as nested dictionaries in python, and sexps in both lisp and python. I could
do it trivially in x86 if needed. Maybe sexps are "expression trees", but it
seems almost any language can implement this pattern. It was much nicer with
data literals than without, but data literals aren't a prerequisite.

~~~
jdmichal
Expression trees are a .NET capability to compile a series of expressions
during runtime. It's not required for this at all; it's just an optimization.
It allows the complex tree of Specification objects to be compiled down into a
simple delegate function, rather than evaluating by walking the tree.

