产品设计过程当中,经常会遇到需要制定各种规则的场景,例如如何选择承运商、如何匹配费用等等。这里记录下关于这种面向用户配置的规则引擎设计的思考。
本质
将 IF 或 CASE 的条件与结果可配置化,UI 化或结构化(使用 json\SQL)如下内容:
- 对象
- 比较运算符
- 值
- 逻辑运算符 与 或 非
- 计算顺序
- 代码执行顺序
- 结果
对象
数据库字段、实体类属性在前台对应业务属性的映射。
- 通常需要决定那些实体类,哪些属性可以用于制定规则。如果将这个也变成自由定义,可能会过度增加复杂度。
- 可能会遇到多个对象进行数学计算过后再进行比较运算的情形,例如体积满足某个条件,但是数据库只单独记录了长、宽、高,在设计时需要额外注意。
- 前台设计时,应该使用下拉框,避免输入。
比较运算符
- 使用下拉框。
- 支持常见的数值型运算符和字符串运算符。
- 如果对象选择是数值型的属性,那么运算符应该候选项限定为数值运算符。
- 尽量不使用 isNull 之类的运算符,不太适合暴露给普通用户。
值
如果对象是数值型,在前台进行输入合法验证。
逻辑运算符 与 或 非
- AND OR NOT 且 或 非。
- 使用下拉框。
计算顺序
- 自然顺序,不需要设计。
- 自定义顺序,需要使用括号。
- 用户可能很容易理解括号,但是不一定能够记得其他运算符的先后顺序,可能的话,将用户配置的规则的计算顺序在前台展示出来。
代码执行顺序
- 可以将 CASE WHEN THEN ELSE 或 IF THEN ELSE 这样的关键字或对应的关键字中文描述暴露给用户,如果确定用户可以理解的话。
- 使用规则优先级数字,交由用户填写。
- 应该提示给用户,数字大小与执行顺序的关系。
- 规则应该按照执行顺序进行排序。
- 如果 UI 设计上,不同条目的规则不可以拖拽进行优先级调整,那么优先级应该选择数值型,以便插入规则时不需要修改后续所有规则。
- 如果规则已经使用整数型作为数据类型,在配置时可以使用 100,200 这样的大间距整数,当需要插入新规则时,取前后规则的中间值即可,而不需要调整所有后续规则。
- 对优先级进行唯一性检查。
- 注意这些分支语句都应该有兜底。
结果
问题
新增一条规则后,怎么知道规则逻辑对不对?
可以在规则引擎的配置页面增加一个测试功能,允许用户使用某个历史数据验证匹配的具体规则来验证,也可以允许用户手动输入测试的各属性值的组合来验证。
新增一条规则后,可以验证该命中规则的命中了,怎么确保不该命中规则不命中呢?
每次规则被更新,都选择历史一段时期的数据重新匹配一次规则,并输出和之前匹配的目标结果不一致的数据,交给用户验证。
规则太多,管理困难,新增编辑规则时,完全不知道相关的规则有哪些,优先级应该如何制定
- 规则太多就 divide and conquer,看看规则是不是可以明显的进行分组,把最常用的区别大的因素作为规则头的判定对象,其他的作为明细的判定对象。如此一来只需要在组内的少数规则中判断如何设置优先级即可。
- 每次新增规则后,采取历史数据回归验证的方式检查正确性。
好的实践
- 记录命中规则的原因与结果。
- 使用缓存机制。如果规则长期稳定,使用缓存可以避免遍历规则。例如使用重量、体积范围决定承运商的规则,可以记录下来相同重量体积区间的记录,避免重复计算。
- 每条规则鼓励用户填写规则描述,用直白的语言便于事后理解与核对规则设定是否满足意图。
- 注意规则应用的场景是否经常有时间段限制,如果有,允许用户配置生效时间范围。对于时间要求高的场景,不能寄希望于用户在时间点到达那一刻再去配置规则,必须要提前配置。有时间段限制的规则,需要考虑规则延期或制定新规则的提前期,必须在提前期前提醒用户开始未来时间段规则的制定。
- 规则使用版本号,修改应该记录下修改前后的内容日志,用于事后溯源。
- 对生产环境影响很大而且破坏不可逆的规则,应该提前考虑如何在测试环境对线上的数据进行模拟。
- 对规则设定是否启用的开关,并且应该提供规则不生效时,如何达到原目标的替代方案。
- 一个规则可能会出于管理考虑需要人工选中规则输出结果,设计时需要考虑。例如:
- 计费规则,使用去年的业务数据,运行一次今年的新报价计费规则对比
- 同样的业务数据,分别使用不同供应商的规则,进行结果对比
我所说的一切都只是我的观点。
我的观点都可能是错的。
访问量统计:0