Open RovingSea opened 6 months ago
@startuml participant 上层业务 autoactivate on
上层业务 -> 规则引擎 #005500: 规则组Code
规则引擎 -> 数据库: 规则组 Code return 规则集
规则引擎 -> 规则引擎: 初始化上下文 return
规则引擎 -> 规则引擎: 将规则集脚本作为 Bean 注入 Spring 容器中 return
规则引擎 -> 规则引擎: 执行规则 return 结束
规则引擎 -> 上层业务: 执行结果
@enduml
一、应用背景
上层业务中,往往会出现校验的场景,例如有如下场景: 以社交软件的用户交流、用户更换名称和头像为例:
如果以强耦合的做法会导致业务代码中出现冗余的校验逻辑, 例如更换用户信息时,对新名称进行校验的代码:
这么做会有如下几个缺点:
因此,这是不可取的。
从软件的设计角度出发,校验逻辑不应该耦合于业务逻辑中,简言之, 需要满足每次新增/删除校验规则都不用修改业务代码
因此,需要一款业务组件,既可以帮助业务代码摆脱耦合校验逻辑,也可以 灵活地配置规则的数目和逻辑。
至于有哪些规则?校验的内容是什么?在业务代码是无感知无需关心的。 如下代码:
上述代码中,更换名称的业务代码中传递了一个枚举
VerificationAction.CHANGE_USERINFO
和一个校验模型User
给到规则引擎,再通过规则的校验结果来决定是否成功。无论如何新增/删除校验规则都不需要更改方法
private boolean updateUserInfo(User user)
内的逻辑。这样就满足了开闭原则,同时提高了代码的可读性。
二、需求内容
(一)规则定义
已知有如下要求:
因此可以带入脚本的思想,将规则的代码作为待执行的脚本放到库中存储, 紧接着规则引擎将这些这些待执行的脚本作为 spring bean,随用随取。
在此,优先推荐 groovy 脚本文件。
除此外,规则应该还包含唯一标识,名称,描述,脚本类型(暂时只完成 groovy)
(二)规则引擎可以通过标识确认要执行的规则
根据上述案例,业务代码中给规则引擎传递了一个枚举
VerificationAction.CHANGE_USERINFO
和一个校验模型User
。首先,我们需要确定要执行的规则有哪些。
在此之前,我们可以提出一个定义,多个规则的集合称之为规则组, 可以以上层业务传递的枚举的
code
作为规则组的唯一标识。又因为一个规则组可能会有多个规则,而一个规则可能也会存在于多个规则组中,如:
所以规则组与规则建立的是多对多关系, 因此,需要衍生新表,也就是规则关联表。
在关联表中可以通过规则组 code 确定其要执行的规则有哪些。
总结,通过枚举
VerificationAction.CHANGE_USERINFO
确定要执行的规则。(三)校验对象
在(二)中,我们分析了枚举
VerificationAction.CHANGE_USERINFO
的作用,接下来我们来分析如何将
User
放到规则中进行校验。首先,我们可以参考 IOC(控制反转) 的思想,我们在使用
Spring
框架时, 对象的创建和对象之间的调用过程,是交给Spring
管理的, 我们只是使用了注解进行定义,即可在Spring
容器的中获取到被注解标注的Bean
。那么,我们也可以参考这种做法,把要校验的对象视为
Bean
,然后我们自己再写一个上下文, 需要满足以下功能:@RuleModel
, 凡是上层在方法上标注了@RuleModel
的,将该方法的返回结果自动装配到上下文中完成了校验对象如何装配到上下文中,还需要再完成一步。
就是规则内容中如何获取这个校验对象。
我们可以让所有规则实现一个接口
RuleProcessor
,代码如下:在方法
run
的入参中,从context
获取要校验的对象,如校验用户名称是否含有非法符号, 代码如下:注意,这个代码是在脚本中完成,以 Groovy 完成,代码内容存在规则中,见(一)规则定义。
总结,通过 IOC(控制反转) 的思想,将编写规则的动作脱离业务代码,上层可以通过 手动推校验对象,也可以通过约定的方式确认校验对象。
(四)如何获取规则
在(二)的理论基础上,我们可以定义三张表,规则组、规则、规则关系表, 我们通过规则组 code,先在规则关系表中查询出该规则组下有多少规则, 在通过每一个规则 code,查询出每一个规则的脚本内容。
(五)简化上层同步规则步骤
上层在选定的目录下编写
Groovy
脚本,并可以通过调用规则引擎服务的接口,完成同步功能。 目的是方便上层在开发时,可以在idea
中编写脚本,而不需要切换开发工具到数据库中将规则内容进行更新。三、整体流程时序图
将下述代码粘贴到此处,所见即所得