A clinical decision support system, seen from the software side, is a rule engine plus a binding to patient data. The engine — the part that evaluates conditions and derives conclusions — is a problem that decades of work on production systems have already closed; the data binding is where clinical standardisation has run aground more than once. I’ll try to keep the two layers apart by taking as a reference an engine that has nothing to do with healthcare: Drools 4.0, out on 25 July 2007.
Context
The formalisms born inside healthcare to write clinical logic — Arden Syntax (an HL7/ASTM standard), GELLO, GLIF, PROforma — have produced technically sound specifications with modest operational uptake. Arden Syntax writes rules as Medical Logic Modules (MLMs) and for fifteen years has carried a flaw known as the curly braces problem: references to local clinical data sit inside curly braces { }, and the syntax that queries them is decided by the individual record system, so they are not portable. An MLM written for one hospital does not run elsewhere unless the contents of the braces are rewritten first. GELLO, an object-oriented expression and query language, was conceived partly for this: to fix a common data model on which conditions are written. It remains a language with very few implementations in production.
The general IT industry, meanwhile, built rule engines indifferent to the domain, with broad user bases and mature tooling. On the JVM the most widely used Open Source one is Drools, started by Bob McWhirter in 2001, taken in by the JBoss community in 2005, and brought inside Red Hat with the 2006 JBoss acquisition. The concrete question is whether a general-purpose engine covers the “engine” layer of a clinical system better than the purpose-built formalisms do.
Architecture
Drools keeps facts and rules well apart. Facts are plain Java objects (POJOs, Plain Old Java Objects) that the calling application injects into a working memory; rules are written in DRL (Drools Rule Language) as condition-action pairs. A rule has this form:
rule "warfarin + NSAID, bleeding risk"
when
$p : Patient()
$w : ActiveDrug(ingredient == "warfarin", patient == $p)
$n : ActiveDrug(klass == "NSAID", patient == $p)
then
insert(new Alert($p, "Interaction: anticoagulant + NSAID", Level.HIGH));
end
The when block is the Left-Hand Side: a sequence of patterns the engine tries to unify against the facts in memory. The then block is the Right-Hand Side: the actions that modify the working memory or call external services. The application loads the facts, calls fireAllRules() and collects the conclusions.
Under the bonnet Drools applies the Rete algorithm, which Charles Forgy described in a 1974 working paper, his 1979 thesis and the formal 1982 paper (Rete: A Fast Algorithm for the Many Pattern/Many Object Pattern Match Problem, Artificial Intelligence 19). Rete builds a discrimination network across the conditions of all the rules, within which facts are propagated and filtered in small steps. When you add or retract a fact, nothing is re-evaluated from scratch: only the portion of the network the change touches is reworked. It is this graduality that makes Rete suited to a working memory that changes often, that is, the case of an active record where drugs come and go and lab results arrive.
Drools 4.0 rewrites the Rete implementation with the surrounding tooling, adds conditional elements (from, set up for Hibernate sources, plus collect, accumulate, forall), a DSL mechanism for building on top of DRL a syntax closer to the domain language, spreadsheet decision tables and a web BRMS (later Guvnor) for writing rules collaboratively. The licence is Apache 2.0.
The critical point
The “engine” layer is covered. What is left uncovered is the data binding, and Drools throws it entirely outside its own perimeter: facts are arbitrary POJOs, so it falls to the application to translate record data into Java objects before injecting them. Drools imposes no clinical model.
It is the curly braces problem in another guise. Arden leaves the binding implicit inside the braces; Drools leaves it explicit but empty, to be filled with adapter code. In both cases the piece that decides whether a rule library is portable — the data model the rules are written against — is not supplied by the engine. Two hospitals adopting Drools with two different fact models cannot exchange rules, just as two Arden MLMs with different queries inside their braces cannot.
The route to closing the problem runs not through the engine but through a common data model to inject as facts. The discussions under way point to the HL7 virtual Medical Record (vMR) as a candidate: a set of standard objects — demographics, active drugs, problems, lab results — that an adapter fills from the record and that the rules take as given. Rules become portable to the extent that they are written against the vMR rather than against local entities. That is a standardisation of the data, not of the engine.
Implications
Keeping the two layers apart has practical consequences. The execution engine stops being a clinical research problem and goes back to being an engineering choice with the usual criteria: performance, licence, ecosystem. Rete on Drools holds thousands of rules over working memories of tens of thousands of facts; the Apache 2.0 licence lets the engine be embedded in products that will have to qualify as medical devices, with the regulatory work that follows; integration with enterprise Java stacks is direct. These are claims you can verify, not promises.
Several experimental adoption lines sit well with this separation: evaluating automatically a trial’s inclusion and exclusion criteria at enrolment; checking drug-drug interactions at prescription; firing time-based reminders for screening and follow-up; verifying adherence to care pathways for chronic conditions. In some academic projects Drools acts not as a clinical language but as the engine that runs more abstract models (GLIF, PROforma) compiled down to rules: the general-purpose engine sits beneath the clinical formalism rather than in its place.
Limits
The separation has a price. The POJO fact model is expressive but knows nothing about clinical matters: nothing in Drools knows what a drug or a diagnosis is, and all the terminology (codings, units, reference ranges) ends up in the code that builds the facts. The curly braces problem is displaced, not solved: without a vMR that is actually adopted, every project rebuilds its own model and rule libraries do not circulate. What is most lacking in the CDS ecosystem is not the engine but a shared, maintained library of clinical rules, and no engine produces one on its own. DRL authoring, finally, stays a developer’s craft: the DSL and decision tables bring the text closer to the clinician but do not remove the need for someone to govern its correctness and lifecycle.
How far a vMR that is actually implemented shifts the balance is still to be seen. As long as the data model is tailor-made for each project, Drools solves the easy half of the problem.
- https://blog.kie.org/2007/07/jboss-drools-4-0-released.html
- https://www.infoq.com/news/2007/08/drools/
- https://en.wikipedia.org/wiki/Rete_algorithm
- https://www.noze.it/en/insights/drools-regole-clinica/
Cover image: Decision tree drawn with flow-chart symbols: condition diamonds branching into yes/no paths toward action boxes — diagram by IPWAI, CC BY-SA 3.0 — https://commons.wikimedia.org/wiki/File:Decision_tree_using_flow_chart_symbols.jpg