前言
我在最近一年经常听到大家在讨论DDD,而且议论纷纷,大家各抒己见。
比如说在某技术微信群讨论,有些人说DDD是噱头,为了搞个业绩(又不是不能跑 😉),还有些表示自己是CRUD Boy 不知道DDD是什么高大上东西。
接下来我用大白话来表达我对DDD的看法。
现状
- 当我们去看招聘网站的时候,会发现有些要求要会领域设计理念。还有些要求会SOA面向服务设计
- 在业务发展到一定体量,业务越来越复杂的时候,我们会发现很多的重复劳动,代码堆得像🚯一样,然后大家还在不断往里面丢代码(PS:这样对代码的可扩展性和维护带来很大的麻烦)
- 曾经一位小伙伴实现了更新某实体,然后记录操作日志的功能,由于很多方法都会更新到这个实体,他在每个方法里面都去记录修改的日志。这就埋下隐患 (⚠️ 你怎么保证别人在其他方法修改实体的时候,也能记录到操作日志呢,也就是太分散)
- 曾经面阿里国际化事业部的时候,被问了问题:你理解的中台化是什么?
大白话辩论
领域设计的作用
Q:针对第一点,不用领域设计行不行?
A:可以不用领域设计,在项目前期的时候,为了将业务快速跑起来,产生价值,这时是顾不上设计的合理性。而且当项目没有到一定的体量,是无法预知到未来会遇到什么变更。当然如果该系统一开始预估的体量就很大,在前期就需要严格去设计好整个架构。
Q:那什么时候需要用上呢?
A: 回到现状第二点,当业务发展到一定体量,业务复杂程度到达一定量的时候,需要用到领域设计来进行改造。这个时候你会发现,大量的代码重复copy,扩展性很差,每次新增需求的时候,都要不断重新开发,这时需要进行领域改造。
Q:第三点导致的原因是什么,怎么解决?
A:本质问题就是,每个方法都会影响到这个实体的更新,导致影响面很大,无法收集到一个点。我们的解决方法就是,将这些更新的地方,全部收集到一个方法里面,然后其他方法只是去调用该变更方法。这样实现日志记录,我只需要去监控一个方法即可!
DDD 精髓
还是大白话讲,就是高内聚,低耦合
PS :我见过很多人上来就是一堆理论,当然理论熟悉也是好事,这样执行起来有条理。
但是🤔 很多知识学到最后就是思想、逻辑、本质,其他都是它幻化出来的东西。这是比较玄学的,顺便讲下我的想法:虚的东西其实是在指引大的方向,比如说企业的愿景,你说它能给业务起作用吗?没有。它是为了给所有行动起到方向的意义。实的东西是为了落地,在落地的时候,又不是用虚的,这样就无法实施。
从实质去讲讲DDD
- 高内聚,低耦合
Q:为什么需要高内聚,低耦合呢?
我们可以看现状的第三点,其实就是他的变更方法扩散开来了,这样导致很多地方可以去修改方法。所以我们需要去将这些方法抽取到一个方法,这就是高内聚,我一个领域里面,它有什么功能,有什么属性,然后外界是没有这个领域有的功能
Q:低耦合又是啥?
举个常见的例子,A逻辑之后->B逻辑,那么问题来了,哪天我不用B逻辑,我要换C逻辑,但是B跟C我可以根据情况来切换。
有些大聪明站起来说:可以用策略模式。没错,但是它的本质是什么,就是低耦合。A是不知道B,也不知道C的具体实现,它只知道接口,具体它怎么实现,由适配器,什么规则去注入对应的实现类。
Q:既然低耦合的好处是什么?
为了可扩展性,当某天我不用某个中间件,而采用其他中间件,其实我只需要写另一个适配器就解决这个问题。如果代码跟其他中间件强耦合,那GG,你得去改写之前代码,随着中间件越来越多,代码的可读性也会越来越差。
- 我理解的中台化是什么?
🎄 中台化,SOA面向服务架构,其实都是一个思想,分而治之。一个服务里面,会有这个业务领域对应的相关操作,有对应领域模型,领域对象,值对象,上下文,然后对外提供服务,这体现高内聚,把对应的功能点收拢到一个服务里头进行管理。
🎃 中台概念里头,有个大前台,领域概念里头有个应用层。它作用是干嘛的?是做编排。就是我每个服务都有对应的功能,那么一个业务下来肯定会有很多不同服务来提供功能,需要一个地方来对他们进行编排。
Q:大前台的作用是什么?
在单体服务里头,我们通常写在service里面,像现状第三点,每个方法或者每个服务都有编排任务,这样的话就有可能每个服务都会有重复的代码以及相似功能,其实我们需要去抽象出来,变成一个单独的服务,这就是大前台
从理论上讲讲DDD
概念
- 领域:领域是相对于业务领域,或者问题域,范围概念。一个领域会包含多个子域,比如说订单域,会有商品、订单等等子域
- 限界上下文:既然有范围,那么就有边界,领域之间靠context上下文来进行通讯
- 实体、值对象:实体对应PO,值对象,在实体基础上会加上其他属性
- 聚合,聚合根:对实体、值对象会进行聚合,聚合根,可以通过这个访问内部实体的东西,不会直接操作跟内部实体
- 防腐层 ACL:限界上下文处理外界上下文的时候,需要做业务处理
贫血模型
里面只有属性,但是它的校验、属于它方法放在外面。违反单一原则,封装性比较差的
充血模型
里面包括属性,以及对应的方法实现。这样的好处,开闭原则,以后扩展的话在类里头新增方法。放在外面去做的话,其实是修改,应该减少修改的情况
---
Domain Primitive
在很多业务中,我们面向过程,比如说一个方法(String,String) 参数,他就会有前后顺序,分别代表什么
如果我们把它封装成对象的话,可以减少这样的问题。
原则
- 隐形概念显式化 ,比如说聚合根,对于一个东西,它有唯一的标识。如果是前者的话,可以是name,phone等等做标识
- 隐式上下文显式化
- 封装多对象
这就是为什么有很多值对象,聚合根,entity,其实是为了封装对应类,以及相关的方法,符合开闭原则
DDD分层
经典分层
接口层、应用层、领域层、基础层
接口层:面向接口,controller
Application 属于编排,就是其他层都封装各自小单元类,他们执行的顺序由应用层来处理
应用层:面向用例,就是会管理各个领域的调用关系,比如说校验完->A领域->B领域
领域层:有领域服务,以及对应子域。处理该领域的业务逻辑
基础层:处理数据库、缓存、第三方等等
六边形架构
概念上就是以适配器,来达到低耦合的效果,外界通过适配器访问内部接口,向内进行依赖
Q:向内依赖是什么?
越到底层,耦合越高,是我具体实现哪里,比如说我用到什么中间件,这里耦合是最高的。越到外面,就变成抽象接口,适配器,供外部去调用。
依赖倒置分层
基础设施层都是接口,而domain去实现对应接口功能。这样依赖的方向是向外,我用什么中间件,由domian层去解决注入什么类型。
Q:这样设计的好处是什么?
我们很多框架都强依赖第三方框架,比如说mybatis、mq等等中间件
那么它有什么坏处呢?就是当我换其他组件的时候,会改很多东西
domain层设计成接口的好处,就是具体实现不会强依赖其他组件,随时可以另外实现
domain | 包含内容 | 描述 |
---|---|---|
Repositity | 映射mapper以及扩展,避免强依赖底层 | |
ACL | 防腐层,处理跟外部不一样的东西,比如说协议、api、缓存 |
按照个人见解画的分层图
总结
DDD目的是为了高内聚,低耦合。它通过业务领域的划分,然后在领域里面实现对应的功能,实现高内聚。然后通过适配器,依赖倒置,实现低耦合。
在业务体量到达一定程度,还有业务复杂度比较高的项目,是有比较好的扩展性。
觉得有帮助的,请关注下博主,感谢~🧧🧧🧧