一、定义
DDD是Domain Driven Design(领域驱动设计)的简称,这是一种软件设计和开发的方法论,一般适用于复杂业务领域软件设计和开发。
过去系统分析和系统设计都是分离的,正如我们国家“系统分析师” 和“系统设计师” 两种职称考试一样,这样割裂的结果导致,需求分析的结果无法直接进行设计编程,而能够进行编程运行的代码却扭曲需求,导致客户运行软件后才发现很多功能不是自己想要的,而且软件不能快速跟随需求变化。
DDD则打破了这种隔阂,提出了领域模型概念,统一了分析和设计编程,使得软件能够更灵活快速跟随需求变化。
服务器后端发展三个阶段:
1.UI+DataBase的两层架构,这种面向数据库的架构(上图table module )没有灵活性。
2.UI+Service+DataBase的多层SOA架构,这种服务+表模型的架构易使服务变得囊肿,难于维护拓展,伸缩性能差。见这里讨论或Spring Web 应用的最大败笔.
- DDD+SOA微服务的事件驱动的CQRS读写分离架构,应付复杂业务逻辑,以聚合模型替代数据表模型,以并发的事件驱动替代串联的消息驱动。真正实现以业务实体为核心的灵活拓展。
以比赛Match为案例,提倡充血模型,实际就是让过去被肢解被黑crack的业务模型回归正常,当然这也会被一些先入为主或被洗过脑的程序员看成反而不正常,这更是极大可悲之处。看到领域模型代码,就看到业务需求,没有翻译没有转换,保证软件真正实现“拷贝不走样”。
DDD最大的好处是:接触到需求第一步就是考虑领域模型,而不是将其切割成数据和行为,然后数据用数据库实现,行为使用服务实现,最后造成需求的首肢分离。DDD让你首先考虑的是业务语言,而不是数据。重点不同导致编程世界观不同。
DDD认为很多原因造成软件的复杂性,我们不可能避免这些复杂性,能做的是对复杂的问题进行控制。而一个好的领域模型是控制复杂问题的关键。领域模型的价值在于提供一种通用的语言,使得领域专家和软件技术人员联系在一起,沟通无歧义。
DDD落地实现离不开Clean架构、六边形架构、 CQRS、Event Source几大大相关领域。
二. 专业名词
先介绍一些专业性名词,对于后面的核心点比较好理解。
Bounded context:边界上下文
划分领域边界,边界内领域模型保持一致,强调内聚,并与边界外的领域模型解耦。
Entity:领域实体
有唯一标识,可变的业务实体对象,它有着自己的生命周期。比如User、帖子等。
Value Object:领域值对象
没有唯一业务标识,通常依附于其他领域实体,值对象的内容不可变,要么被整体替换。如:用户点赞行为等。
Aggregate:聚合
是一组业务关联度很强的实体/值对象集合,每个聚合都有一个根实体(Root Entity),通过根实体可以路由到整个聚合。
Domain Event:领域事件
领域中发生的异步处理事件、异步消息通知等,比如:异步写入的登录历史记录。通常借助消息队列实现。
Domain Service:领域服务
当某些业务行为无法归类到某一个Entity/Value Object时,我们便可以创建领域服务来完成。比如:账户转账场景,涉及到两个不同的Account实体,再比如社区的敏感词过滤场景,帖子可以用,评论亦可以用,因此可以抽离到ContentFilter中完成。
Repository:仓库
严格意义上将仓库是基础设施层的东西,但是为了保持领域模型的整体性,我们将仓库的接口定义放到领域中,这样可以在领域中约束实体/值对象的增删改查接口,同时还可以方便地完成仓库的内存形式实现,使得领域模型弱依赖于持久化层。这一点在书中被成为‘依赖倒置’(参考《实现领域驱动设计》P372)。
Factory:领域对象工厂
用于复杂领域对象的创建/重建。重建是指通过respostory加载持久化对象后,重建领域对象。
聚合(Aggregate)
聚合主要用来封装对象的内部结构.其作用就是将关联的对象聚合在一起,由一个对象对外暴露遍历接口.这样能够降低对这组关联对象的维护的复杂度.每个聚合都要有一个根(root)和一个边界(boundary).
为了实现概念上的聚合根,需要对所有事物应用一组规则.
1.根实体具有全局标识,它最终负责检查固定规则.
2.根实体具有全局标识.边界内的实体具有本地标识,这些标识只有在聚合根内部才唯一.
3.聚合外部的对象不能引用除根实体之外的任何内部对象.根实体可以把对内部实体的引用传递给它们,
但这些对象只能临时使用这些引用,而不能保持引用.根可以把值对象的副本传递给另一个对象,
4.而不必关心它发生什么变化,因为它只是一个值对象,不再与聚合根有任何关联.
5.作为上一条规则的推论,只有聚合的根才能直接通过数据库查询获取.所有其他对象必须通过关联的遍历才能找到.
6.聚合内部的对象可以保持对其他聚合根的引用.
7.删除操作必须一次删除聚合之内的全部对象.
8.当提交对聚合边界内部的任何对象的修改时,整个聚合中的所有固定规则都必须被满足.
领域通用语言(Ubiquitous Language)
这个概念我也是第一次看到,作者在整个书中时不时要提一下这个概念.其实领域通用语言主要解决的是沟通问题,主要是业务人员和技术人员的沟通问题.不然开会的时候业务人员说自己的,技术人员说自己的,然后还谁都看谁不爽.
其实,这个东西还是让技术人员妥协了,因为组成领域通用语言的以领域知识的概念为主.当然这也是必然的,除了一些技术性很强的项目(比如搞个云平台),多数项目的目的都是为了服务于业务,帮助公司业务部门为公司创造更多的利润.完成技术人员学习领域知识的过程作者称之为消化知识.
模型驱动设计(Model-Driven Design)
10年听说了TDD,其目的是让每一个功能有一个明确的完成界限.这回又听一个新东西MDD.模型驱动设计其实就是从模型开始出发,将领域通用语言中的概念映射到代码上,这样可以让代码直接体现出领域知识.让技术人员和业务人员的沟通在一件事情上,如果没有这层映射,他们的沟通必然存在转换.
分层架构(Layered Archiecture)
用户界面层(或表示层)
应用层
领域层(或模型层)
基础设施层
资源库(Repository)
将所有对象存储和访问操作交给资源库来完成,而对于聚合一定控制只能从聚合根开始访问遍历.
三、核心要点
-
将业务逻辑都内聚到业务领域层(domain层),将设计和开发的关注点聚焦到业务领域。
-
建立通用的业务领域语言,这是开发工程师和业务领域相关专家沟通的桥梁。
3.战略上,将“有界域”(Bounded Context)解耦各个业务系统/组件,通过‘防腐层(Anticorruption layer)’确保自有业务领域不受外界污染,通过‘开放主机服务(Open Host Service)’向外界公开服务;
- 战术上,将业务对象建模为entity和value object。entity有唯一业务标识且在其生命周期中状态可变,value object与之相反;关联性强的entity和value object聚合成一个Aggregate,每个Aggregate有一个root entity,确保Aggregate内容业务规则和行为的一致性;
业务行为尽量建模在entity/ value object 上,当业务行为无法建模到任何业务entity/value object时,可以使用领域服务(domain service),使用factory创建复杂的业务entity,使用repository实现实体的重建和持久化操作;领域相关的通知等可以通过domain event发布出去。
四、图解
User Interface —— 用户界面层。提供与用户/调用者交互的接口,可以是View,也可以是Restful api,还可以是二进制形式的tcp协议接口等。
Application —— 应用服务层。组合domain和infrastructure,完成具体的业务逻辑。
Domain —— 业务领域层。是ddd中的核心层,内聚所有的业务逻辑,保持领域的一致性。通常他可能会用到infrastructure层的公共组件。
Infrastructure —— 基础设施层。提供公共服务组件,比如validation、登录态校验、trace日志记录等等。
模式:
包含核心概念和实现这张图囊括了DDD中的所有核心概念,上面部分是战术相关,下面部分是战略相关。不再赘述,建议读者直接查阅DDD原书,有详细介绍。值得反复细读。
DDD适用场景
1.业务逻辑复杂的系统;
2.不太复杂的业务系统没有必要使用DDD,比如内容管理系统,只是对内容进行简单的增删改查;再比如渠道接入层系统,只是做请求的转发和翻译,没有核心业务逻辑,就没有必要使用DDD。
3.有资深的业务领域专家支持,需要有一个很懂业务的人,在业务上提供指导性的意见;
4.工程师都认可DDD设计思路,并积极主动地探讨和迭代,将业务模型日趋完善;
5.项目工期压力小可以接受。
DDD带来的好处:
业务逻辑内聚到业务领域层,可以更好的保证业务规则的一致性;业务逻辑被合理的分散到不同的领域对象中,代码结构更加清晰,可读性,可维护性更高。
对象职责更加单一,内聚度更高。
系统高度模块化,代码重用度高,不会出现太多的重复逻辑。
软件的可维护性和可扩展性增强,工程师可以聚焦在业务领域层,并致力于业务领域模型的迭代和维护,适应新业务的发展;
软件的可测试性增强,领域层代码不需要借助用户接口层的入口便可以独立测试业务逻辑,通过repository的哑实现可以摆脱对数据持久层的依赖;
软件工程师和业务领域专家(产品经理)使用同一种语言交流,沟通成本降低,提升效率;【这一点在现实中或多或少在使用,但是比较模糊,很多时候大家会用到‘领域’这个词语,但是并不会刻意地坐下来讨论各自领域应当包含哪些实体......这也是大家可以进步的地方。】
业务领域层代码可以独立出来,共享到其他地方,可以轻松将服务产品化,云化;【这一点是yy出来的,可忽略。】
DDD不好的地方:
对工程师有较高的业务建模技能要求,期望他们能从复杂的业务上下文中识别出正确的业务模型,并将各个业务行为归入合适的entity/value object/domain service中;
项目前期需要投入更多的时间进行业务建模,不能快速进入开发阶段;
需要资深的业务领域专家参与进来,否则业务模型的频繁重构(重大改动)会带来额外成本;
参考:
领域驱动设计精要
网友评论