2011-11-16 Logic Planning Discussion
Requirements/Goals
Existing logic engine can be implemented as an optional module without too much trouble.
A shared Rule interface
Rules provide a method to evaluate within a context + parameters
A shared Result interface
Result declares it's type explicitly
Provides a mechanism for consumers to easily distinguish between & use lists vs. single values
Provides an easy way to coerce results between different types and between lists vs. single value
A shared LogicContext interface within which rules are evaluated
Centralized token registration by providing token, rule ID, provider, and configuration (unique across providers)
Support for parameters.
Caching is deferred to rule evaluators for now
Skeleton Interfaces / Implementations
Rule
interface Rule {
public Set<RuleParameterInfo> getParameterList(); // TODO: We didn't discuss this interface, but we did agree that rules need to support parameters
}
RuleEvaluator
interface RuleEvaluator {
boolean canEvaluate(Rule rule); // TODO: This might be better implemented through annotations. Needs further discussion
Map<Integer, Result> evaluate(Cohort, Rule, Map<String, Object>, RuleContext); // TODO: We probably want to wrap Map<Integer, Result> in a proper class
}
RuleProvider
interface RuleProvider {
Rule getRuleInstance(String ruleName, String extraConfig); // ruleName could be anything the provider wants. might typically be a classname.
}
RuleService
RuleContext
Result
Use of Result
For coercing / converting Results to scalars for use in comparisons etc, for example:
if (eval("BMI") > 23) { ... }, we discussed a few possible approaches: (TODO: Decide on one)
Add methods like "asDouble()", "asDate()", "asString()", "asBoolean()" to the Result interface (as we have now)
Add single method to Result like: eval("BMI").coerce(Double.class) > 23
Add utility method like: LogicUtil.toDouble(eval("BMI")) > 23
Add utility method like: LogicUtil.coerce(eval("BMI"), Double.class) > 23
Add a variety of converters like: new DoubleConverter().convert(eval("BMI")) > 23
Benefit of #5 is that it is cleaner than a massive utility method with lots of conditional logic, and that it allows modules to plug new converters into the framework as needed. It might look something like this:
We ran out of time before we could really discuss next steps on all of this. Should we carve out time in another design forum in December?