规则树
前言介绍
在上一章节介绍了领域驱动设计的基本概念以及按照领域驱动设计的思想进行代码分层,但是仅仅只是从一个简单的分层结构上依然没法理解DDD以及如何去开发这样的微服务。另外往往按照这样分层后依然感觉和MVC也没有什么差别,也没有感受到带来什么非常大的好处。那么问题出在哪呢?我个人觉得DDD学起来更像是一套指导思想,不断的将学习者引入到领域触发的思维中去,而这恰恰也是最难学习的地方。时而感觉会了,而实际开发中又不对,本来已经拆解清晰了,但怎么又那么像MVC了。甚至怀疑自己,我在干嘛?
无论是DDD、MVC,他们更像是家里三居或者四局的格局,每一种格局方式都是为了更好的实现对应架构下的设计思想。但,不是说给你一个通用的架构模式,你就能开发出干净(高内聚)、整洁(低耦合)、漂亮(模块化)的代码。这就像是你家住三居、他家也住三居,但是你们屋子的舒适情况就一样吗?{再有,你家里会把厕所安在厨房吗?但你的代码是否这么干过,不合理的摆放导致重构延期。}
另外DDD之所以看着简单但又不那么好落地,个人认为很重要就是领域思想,DDD只是指导但是不能把互联网天下每一个业务行为开发都拿出来举例子给你看,每个领域需要设计。所以需要一些领域专家{产品+架构+不是杠精的程序猿}来讨论梳理,将业务形态设计出合理的架构&代码。
#案例目标
本案例通过一个商品下单规则的场景来进行演示DDD;
- 假设产品需求业务运行人员可以对不同的商品配置一些规则,这些规则可以满足不同用户类型可以下单不同商品。
- 另外一些行为规则是会随着业务发展而增加或者变动的,所以不能写死{写死太吓人了}。
- 数据库的PO类不应该被外部服务调用,这也是必须的。如果你开发过很多系统,那么可能已经吃过亏并意识到这个问题。
- 按照DDD思想我们尝试需要设计一个规则引擎的服务,通过给外部提供非常简单的接口(application)来获取最终结果。
- 通过这样的案例可以很容易的感受到目前的四层架构确实在实现DDD思想上有很多的帮助。
如图;DDD分层结构 | 指导设计架构
#DDD思想 · 开发设计
通过领域驱动设计的思想,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型。那么在技术实现上就需要去支撑这种建模,以使我们的代码模块独立、免污染、易于扩展。
在上面我们提到需要开发一个可扩展使用的规则树,那么如果只是单纯的一次性需求,最快的方式是if语句就搞定了。但是为了使这个领域服务具备良好的使用和扩展性,我们需要做些拆分,那么如下;
- 你是否想过系统在过滤过则的时候其实就像执行一棵二叉树一样非左即右侧,每一条线上都有着执行条件,通过判断来达到最终的结果。
- 按照树形结构我们将定义出来四个类;树、节点、果实、指向线(From-To),用于描述我们的规则行为。
- 再此基础上需要实现一个逻辑定义与规则树执行引擎,通过统一的引擎服务来执行我们每次配置好的规则树。
如图;领域开发设计服务
#工程模型
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
演示部分重点代码块,完整代码下载关注公众号;bugstack虫洞栈 | 回复DDD落地
#application应用层
application/MallRuleService.java | 应用层定义接口服务,也可以适当做简单包装
12345678910111213141516
#domain领域层
domain中有两个领域服务;规则树信息领域、规则执行领域,通过合理的抽象化来实现高内聚、低耦合的模块化服务
domain/service/MallRuleServiceImpl.java | 领域层中的service来实现应用层接口
123456789101112131415161718192021222324252627282930
domain/service/logic/LogicFilter.java | 逻辑决策定义
123456789101112131415161718192021222324
domain/service/engine/EngineFilter.java | 引擎执行定义
12345678910
#infrastructure基础层
1、实现领域层仓储定义 2、数据库操作为非业务属性的功能操作 3、在仓储实现层进行组合装配DAO&Redis&Cache等
infrastructure/repository/RuleRepository.java
12345678910
#interfaces接口层
1、包装应用接口对外提供api 2、外部传输对象采用DTO类,主要为了避免内部类被污染{不断的迭代的需求会在类中增加很多字段} 3、目前依然是提供的Http服务,如果提供的rpc服务,将需要对外提供可引用jar
interfaces/DDDController.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
#测试验证
规则树结构{数据库转Json} | 可自行定义
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
通过postman调用 | raw => json
查询规则树信息 测试接口:http://localhost:8080/api/tree/decisionRuleTree 请求参数:{"treeId":10001}
12345678910111213141516171819
规则树行为信息决策 测试接口:http://localhost:8080/api/tree/decisionRuleTree 请求参数:{"treeId":10001}
1234567
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
#综上总结
- 以上模拟购物场景下的规则处理抽象为树决策引擎,以达到独立领域服务。另外决策服务可以使用drools (opens new window),任何抽象并不一定永远使用,不要拘泥于一种形式
- 一些大型架构设计往往不是换一个设计模型就能彻底提升效率,还是需要人员整体素质,这是一个不断培养的过程
- 领域驱动设计的思想并不只是教会程序猿写代码,也是非程序员以外的所有互联网人员都适合学习的内容
- 家里住的舒适不舒适,并不一定取决于三居或者四居,大部分还是依赖于怎么对格局的布置。事必躬亲、亲力亲为的精益求精之路,终究会让你设计出更加合理的代码
微信搜索「bugstack虫洞栈」公众号,关注后回复「DDD落地案例」获取本文源码&更多原创专题案例!此外推荐你一个可以上手实践的DDD项目,看看如何从流程中提炼领域设计代码实现,在应用层、领域层以及基础层的仓储实现是如何完成开发和调用的,项目地址:https://t.zsxq.com/jAi2nUf
Loading...