DDD领域驱动设计落地实践 -- 知识铺
DDD领域驱动设计概述
DDD领域驱动设计是一种软件开发方法,它着重于以业务领域为中心进行软件系统的分析、设计与开发。这种方法由Eric Evans在2003年提出,其核心思想是创建一个领域模型,以指导软件的开发过程。以下是对DDD领域的详细介绍和实践指南。
一、引子
领域驱动设计(DDD)之所以受到关注,是因为它提供了一种不同于传统软件开发的新思路。在公司领导的推荐下,作者投入了5个月的时间深入学习和实践DDD,体验了从入门到放弃的全过程。
二、核心思想
学习资源- 网站:领域驱动设计学习网站- 书籍:《领域驱动设计》
2.1 服务器后端发展的三个阶段1. 面向过程脚本:在业务简单时容易实现,但随着业务复杂度的提升,维护难度迅速增加。2. 面向数据库表:目前市场上的主流做法,业务复杂后维护难度逐渐增加。3. 面向业务模型:采用DDD结合SOA微服务架构,事件驱动的CQRS读写分离,以聚合模型替代数据表模型,以并发事件驱动替代传统的消息驱动,实现真正的业务实体核心化。
2.2 DDD的最大特点DDD的革命性在于其领域模型能够准确反映业务语言,与传统的贫血模型相比,DDD采用的是充血模型,即业务方法定义在实体对象中。
三、实践落地### 3.1 领域模型设计领域模型设计包括战略设计图和战术设计图,分别从宏观和细节两个层面分析业务场景。
领域战略设计图- 从限界上下文角度分析,关注核心域、子域和实体之间的关系。
领域战术设计图- 细化到核心业务字段、领域实体、值对象、领域服务和领域事件等,为代码编写提供指导。
3.2 技术实现DDD项目框架采用四层分层结构:
- 展现层:controller层,不包含业务逻辑。2. 应用服务层:可以包含查询逻辑,核心业务逻辑下沉到领域层。3. 领域服务层:业务组装和仓储接口定义的地方。4. 基础设施层:实现仓储和PO持久化。
3.2.3 技术架构目前业界尚无统一标准,可以参考github上的开源项目进行学习和实践。
四、总结尽管DDD提供了一种新的软件开发思路,但作者建议不要在主流业务中过度依赖DDD,而应该适度尝试,避免完全替代现有的开发模式。
服务器后端发展三个阶段
1. 面向过程脚本- 特点:初始简单,但随着业务复杂度的提升,维护难度迅速增加。- 现状:已基本不被主流采用。
2. 面向数据库表- 特点:开始时难度适中,但随着业务复杂度的增加,维护难度会在一段时间后迅速上升。- 现状:目前市场上的主流做法。
3. 面向业务模型- 架构:DDD(领域驱动设计)结合SOA(面向服务的架构)的微服务,采用事件驱动的CQRS(命令查询职责分离)架构。- 优势:使用聚合模型替代传统的数据表模型,以并发的事件驱动方式取代了传统的串联消息驱动,真正实现了以业务实体为核心的灵活拓展。- 特点:虽然初始难度较高,但随着业务复杂度的提升,维护难度的增长是线性的,这已经是一个相对较好的结果。
DDD的最大特点
DDD的革命性在于其领域模型能够准确反映业务语言。与传统的微服务数据对象相比,DDD的领域模型是充血模型,即业务方法定义在实体对象中,而不是简单的setter/getter方法。
落地
3.1 领域模型设计
以渠道中心作为一个微服务的示例来进行领域模型设计。设计的核心是两个图:战略设计图和战术设计图。
1. 领域战略设计图- 定义:从限界上下文的角度出发,分析业务场景,主要展示宏观上的核心域、子域以及实体之间的关系。
注意事项- 以上内容为示例,实际设计时应根据具体业务需求进行调整和优化。
示例图- 由于无法提供实际的图形,这里使用文字描述来示意。实际设计中应使用UML或其他图形化工具来展示领域战略设计图。
2.领域战术设计图
战术设计图是从一个限界上下文的角度出发去分析业务场景。细化到核心业务字段、领域实体、值对象、领域服务、领域事件等等。基本上这个图画完,代码已经知道怎么写了。demo如下图:
3.2 技术实现
整体项目框架分层图如下所示:
典型DDD分层结构解析
在领域驱动设计(Domain-Driven Design, DDD)中,分层结构是核心概念之一,它有助于清晰地划分系统的不同职责。以下是对四层典型DDD分层结构的详细解析:
1. 展现层(Controller层)这一层主要负责与用户进行交互,接收用户的输入并返回响应结果。Controller层不包含任何业务逻辑,其主要职责是将用户的请求转发到相应的应用服务层。
2. 应用服务层应用服务层是业务逻辑的初步处理层。它可以包含一些简单的查询逻辑,但核心的业务逻辑必须下沉到领域层。这一层的目的是协调不同服务之间的交互,确保业务流程的顺畅执行。
3. 领域服务层领域服务层是DDD中的核心,所有的业务逻辑都在这里组装和实现。此外,仓储(资源库)接口也在这一层定义。领域层的目的是封装业务规则,确保业务的一致性和完整性。
4. 基础设施层基础设施层包括仓储(资源库)的实现和PO(持久化对象)的持久化操作。这一层主要负责与数据库等外部存储系统的交互,实现数据的存取。
注意事项- 简单查询如果与业务无关,可以直接从应用层穿透到PO层进行查询,无需经过领域层。这种设计允许非业务类操作跨层调用,以提高系统的灵活性和效率。
- DDD本身并不限制非业务类操作的跨层调用,这为系统设计提供了更大的自由度。
通过上述分层,我们可以清晰地看到DDD分层结构的逻辑性和灵活性,有助于构建可维护和可扩展的复杂系统。
2.DTO是不能存在于domain层的,DDD设计不认为DTO是业务对象,entity才是。或者传值简单数据类型也是可以的。
3.2.1 服务调用问题
1.域内调用
领域内调用,随便调用,丝般顺滑。至于实现,可以由一个核心域的仓储实现层(第四层)去实现多个Repository接口。(比如这里A是核心域的实体名,B是支撑域、通用域等)
2.跨域调用
跨域分为
- 1.同上下文跨域:ACL层->Adapter适配器层→调用其它域的repository。—>不得已才使用,不推荐使用。
- 推荐:1.使用领域事件 eventbus来做解耦
- 2.跨上下文(肯定跨域):ACL层->Adapter适配器层->feign调用
3.2.2 包结构
包结构如下:
展开包结构如下:
**展现层:Controller,**仅做接口的入口定义和编排转发,不做任何的业务处理;
**应用服务层:application,**负责接口参数DTO的简单校验,以及DTO和实体值对象的数据转换,对于简单的业务,也可以在应用层加载实体直接执行实体行为方法;
领域层:
- 模型:根据领域模型分析领域内各实体、聚合、聚合根、值对象等,这些对象在*.domain.model定义,实体内的行为方法只负责维护实体自身的生命周期和状态;
- 行为:领域内各实体、聚合、聚合根等,会有相应的行为,在*.domain.model包下定义行为方法;
- 领域服务:领域提供的接口服务,需要定义在*.domain.service包下,业务相关的前置业务判断、多个实体或值对象的行为逻辑处理等,都在领域服务中实现,需要注意的是并不是每个实体都有一个对应的领域服务,但是依赖多个实体的行为方法,最好根据这个业务模块是建立一个领域服务;
- 仓储:领域服务或上层应用服务需要使用到的基础设施层,包括DB、Feign调用等,定义在*.domain.repository下,在*.infrastructure.repository下实现;
**适配层:**在acl包下的feign定义依赖外部的接口,并在acl的adapter包编写转换,由仓储层操作实体时调用;
**持久层:**与常用DAO定义一致,由仓储层操作实体时调用。
3.2.3 技术架构
目前业内没有标杆,github开源地址:https://github.com/jovezhao/nest 。作者不是本人哈,这个项目可以练手DDD。
四、总结
DDD可以尝试,但不建议主流业务硬上。建议浅尝即止。(据我所知,业内连阿里巴巴都没有大面积推广。)
- 原文作者:知识铺
- 原文链接:https://index.zshipu.com/geek001/post/20240730/DDD%E9%A2%86%E5%9F%9F%E9%A9%B1%E5%8A%A8%E8%AE%BE%E8%AE%A1%E8%90%BD%E5%9C%B0%E5%AE%9E%E8%B7%B5--%E7%9F%A5%E8%AF%86%E9%93%BA/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com