背景
在业务部门,我们日常的工作模式是业务提供业务逻辑,我们通过”CRUD“将业务逻辑转成可执行的代码。业务逻辑出现分叉时,就加if-else来解决;当出现多个分支时,就用switch-case来解决;当出现成百上千的分支时,要怎么办?
第一阶段
版本一
简单的办法是通过策略模式,将不同的业务规则归类成一批批策略,每个策略下包含一些规则。不同业务场景选择一个或多个策略执行。这种方式有很好的扩展性,没有增加额外组件所以也有很强的维护性。问题是规则结构没有约束(标准)、规则也无法动态修改。
第二阶段
当工程里出现几百条各种各样的规则时,我们就要考虑运维阶段的可读性了。增强可读性最好的方式是通过约定规则描述的标准,也就是制定规则DSL。DSL是一种受限的领域语言,用来描述规则的结构、行为和约束。一般分为内部DSL、外部DSL、语言工作台。
内部DSL指的是和工程用的相同的语言来描述规则。
外部DSL指的是和工程用的不同的语言来描述规则。
语言工作台值的是通过可视化的方式来配置规则。
下面是简化后的规则类图,我们分别用不同的方式来实现DSL,看看有什么差异。
版本二 - 内部DSL
Java的工程里,可以通过面向接口的方式,每来一个规则就实现一次Condition接口和Action接口。
public class MyCondition implements Condition {
@Override
public boolean evaluate(Facts facts) {
return facts != null;
}
}
public class MyThenAction implements Action {
@Override
public void execute(Facts facts) throws Exception {
// hsf.call()
}
}
public class MyOtherwiseAction implements Action {
@Override
public void execute(Facts facts) throws Exception {
// mq.send()
}
}
// 初始化
Rule rule = new RuleBuilder()
.name("规则1")
.description("这是规则描述")
.when(new MyCondition())
.then(new MyThenAction())
.otherwise(new MyOtherwiseAction())
.build();
版本三 - 外部DSL
可能会觉得用Java描述规则会稍显繁琐,我们也可以用更简洁的脚本语言(Spel/Ongl/Mvel)+ xml/json/yml的方式来描述规则。下面是以Spel + json的形式来描述规则。
[
{
"name": "规则1",
"description": "这是规则描述",
"condition": "#{ ['person'].age > 18 }",
"thenActions": [
"#{ ['person'].setAdult(true) }"
],
"otherwiseActions": [
"#{ ['person'].setAdult(false) }"
]
}
]
版本四 - 语言工作台
内部和外部DSL只是语言层面的区别,在规则发布方式上还是依赖工程的发布。考虑灵活配置和发布场景,就需要引入语言工作台。例如基于qlexpress的规则运营平台,可以在线配置规则,规则的热部署。
第三阶段
版本五 - 业务规则运维平台
上面4个版本的用户都是开发同学,当维护业务规则是业务方的日常工作时,提供一个平台让业务来管理规则会是更好的方式。架构上做到业务逻辑和规则的分离,由开发同学编写业务逻辑,业务同学负责规则维护工作。
这一阶段的功能是面向业务,所以我称之为业务规则运维平台,下面是我实现的一个例子。
小结
版本越高,看似功能越多,其实也越复杂。针对业务部门,业务需求往往灵活多变,在技术选型上还是尽量以简单够用为主。