DDD领域驱动设计
1 DDD是什么?
领域驱动设计(Domain-Driven Design, DDD)是一种软件设计方法论,旨在处理复杂的业务需求和系统设计。由Eric Evans在他的同名书中提出,DDD关注将业务需求和软件架构紧密结合,通过领域模型和相应的设计模式,创建灵活且可维护的系统。
1.1 领域驱动设计的优势
- 解决复杂性:通过将复杂的业务需求分解成更小的子域和限界上下文,DDD帮助开发人员更好地理解和管理复杂性。
- 业务与技术的统一:通过领域模型和共同的语言,业务人员和技术团队可以更高效地沟通和协作。
- 灵活性和可维护性:DDD设计的系统具有更高的灵活性和可维护性,能够更好地适应业务变化。
1.2 领域驱动设计的应用场景
DDD非常适用于具有高度复杂性和业务逻辑的系统,如电商平台、金融系统、物流管理等。但并非所有项目都适合使用DDD,对于一些简单的系统,可能不需要这么复杂的设计方法。
领域驱动设计通过清晰的领域划分和模型设计,应对复杂的软件系统,确保系统的稳定性、扩展性和灵活性。
2 领域驱动设计(DDD)概念
领域驱动设计(Domain-Driven Design, DDD)是一种为了解决复杂软件设计而提出的优秀方案,但并非唯一的方法。
2.1 核心概念
- 领域: 领域是指所有业务规则、定义、范围、知识等的抽象概念。比如,用户支付业务属于交易域,平台提供售后服务属于服务域(或客服域),其他如金融域、人效域、物流域等。
- 分而治之: 通过把复杂的领域(问题)分解成更小的子域,每个子域都有明确的边界和核心实体。通过一系列的拆分、归类、衍生,找到最优解。
2.2 应对复杂性的策略
- 保持内核稳定性和扩展性: 在多变的业务环境中,通过稳定的内核设计,确保系统的扩展性和灵活性。
- 领域模型的共识性: 领域模型贯穿了从设计到交付的软件生命周期,开发、产品、架构师围绕统一的模型进行设计和讨论,确保一致性。
2.3 领域模型的特点
- 图形表达方式: 除了实体模型图的表达比较特殊,其他如时序图、状态机、流程图等表达方式与其他方法并无不同。
- 业务的抽象: 领域模型是对业务的抽象,基于现实但不等于现实。
3 DDD和MVC的比较
在DDD理念提出来之前,最经典的开发模式就是MVC了。
3.1 MVC组成部分
**1.Model(模型)**
-
职责:处理业务逻辑和数据。模型负责与数据库交互,进行数据的CRUD(创建、读取、更新、删除)
-
实现:在图中,Model使用JavaBean来封装数据和业务逻辑。JavaBean类通常包含属性、getter和setter方法,以及一些业务逻辑方法。
2.View(视图)
-
职责:负责展示数据给用户。视图从模型获取数据,并将其以用户友好的方式展示出来。
-
实现:在图中,View使用JSP(JavaServer Pages)来显示前端页面。JSP文件包含HTML代码,并且可以嵌入Java代码来动态显示数据。
3.Controller(控制器)
-
职责:作为模型和视图之间的中介,处理用户输入,调用模型来处理数据,并将结果交给视图进行展示。
-
实现:在图中,Controller使用Servlet来实现。Servlet接收客户端的请求,调用相应的JavaBean处理业务逻辑,然后将数据转发给JSP页面进行展示。
3.2 MVC工作流程
- 用户请求
- 用户通过浏览器发送请求到服务器。
- Controller接收请求
-
Servlet作为Controller,接收客户端的请求。
-
Controller获取用户输入,调用相应的模型进行处理。
- 调用模型(Model)
-
Controller调用JavaBean(模型)进行业务逻辑处理,例如查询数据库。
-
JavaBean执行数据操作,并返回处理结果。
- 将数据传递给视图(View)
-
Controller将处理结果(数据)传递给JSP页面(视图)。
-
JSP页面接受数据并进行展示。
- 显示数据
- 浏览器接收服务器返回的HTMl页面,并将结果显示给用户。
3.3 MVC架构存在的问题
- 控制器过于臃肿:
- 包含大量业务逻辑,导致难以维护。
- 模型职责不明确:
- 业务逻辑、数据访问混杂,模型变得复杂。
- 视图层逻辑复杂化:
- 需要大量数据处理和格式化操作,代码难以维护。
- 模块间耦合度高:
- 各层之间调用关系复杂,难以单独测试和调试。
- 难以应对复杂业务场景:
- 频繁修改代码,系统难以适应新需求。
- 数据一致性问题:
- 难以保证数据一致性,可能导致数据错误。
- 缺乏领域层的抽象:
- 业务逻辑分散,维护复杂业务规则困难。
MVC架构适用于简单业务场景,但在面对复杂、多变的业务需求时,容易出现控制器和模型臃肿、模块耦合度高、数据一致性难保证等问题,导致系统难以维护和扩展。
4 DDD可以带来的价值
4.1 团队价值
-
统一语言
-
清晰的边界定义
-
领域能力沉淀和复用
-
面向业务建模
-
设计和代码的等价
4.2 个人价值
-
提升全局视野
-
提升业务sense
-
构建体系化思维
5 领域驱动设计的核心概念
5.1 DDD完整设计流程
- 提炼业务流程
在这个阶段,目标是对业务进行深入理解和分析,以便为后续的设计奠定基础。这包括:
- 角色:确定在业务流程中涉及的不同角色。
- 业务事件:识别关键的业务事件,这些事件通常是引发系统行为的触发器。
- 参与者:确定业务流程中所有参与者,他们可能是用户、系统或其他外部实体。
- 流程:梳理业务流程,明确各个步骤和环节。
- 描述:对上述元素进行详细描述,确保每个元素都得到充分理解。
- 战略设计
这一阶段主要是从宏观角度对业务进行架构设计,定义系统的高层次结构和边界。具体包括:
- 领域:划分业务领域,定义系统的核心业务领域。
- 子域:将领域进一步细分为子域,每个子域对应一个相对独立的业务模块。
- 通用域:识别通用领域,这些领域包含在多个子域中共享的业务逻辑。
- 支撑域:确定支撑域,这些领域为核心业务领域提供辅助功能。
- 限界上下文:定义每个子域的边界和上下文,明确子域之间的接口和交互方式。
- 战术设计
在战术设计阶段,重点是具体的技术实现和细节设计,主要包括:
- 实体:定义业务中的实体,它们通常是具有唯一标识和生命周期的对象。
- 值对象:定义值对象,这些对象没有唯一标识,通常用于描述某些属性或值。
- 聚合:将相关的实体和值对象组合成聚合,通过聚合根来管理。
- 聚合根:确定每个聚合的根实体,聚合根负责聚合内部的一致性。
- 资源库:定义资源库,用于管理实体的持久化和检索。
- 工厂:使用工厂模式来创建复杂的对象,简化对象的创建过程。
- 领域服务:定义领域服务,这些服务包含跨多个实体和值对象的业务逻辑。
- 领域事件:定义领域事件,捕捉和传递领域内的重要事件。
- 编码
最后一步是根据战术设计进行编码实现:
- 代码分层:根据战术设计将代码进行分层,实现清晰的代码结构。
- 依赖注入:使用依赖注入和其他设计模式,确保系统的模块化和可测试性。
- 单元测试:编写单元测试,确保每个模块和组件的功能正确。
5.2 核心概念
- 领域(Domain):
- 领域是指特定业务范围内的知识、逻辑和规则的集合。它包括所有与业务相关的定义和概念。
- 子域(Subdomain):
- 将大的领域划分成更小的子域,每个子域代表一个特定的业务问题或功能。通过分解,复杂的问题变得更易于管理。
- 限界上下文(Bounded Context):
- 定义领域模型的边界。在每个限界上下文内,特定的术语和规则有明确的含义,不同的限界上下文可能有不同的模型和规则。
- 领域模型(Domain Model):
- 领域模型是对业务领域的抽象和表示,包括实体(Entities)、值对象(Value Objects)、聚合(Aggregates)、领域事件(Domain Events)等元素。领域模型是开发、产品、架构师等各方达成共识的核心。
- 聚合(Aggregate):
- 聚合是具有一致性的聚合根及其相关对象的集合。聚合根负责聚合的状态一致性,并确保业务规则的执行。
- 实体(Entity):
- 实体具有唯一标识和可变状态,是业务操作的核心对象。
- 值对象(Value Object):
- 值对象没有唯一标识,通常是不可变的,代表特定的业务值。
- 领域服务(Domain Service):
- 领域服务封装了跨实体或值对象的业务逻辑,提供特定领域的操作和功能。
6 领域建模
领域建模是DDD最关键的部分,它的目的归纳起来就是:提炼业务知识,形成统一语言,沉淀领域模型。
领域建模是领域驱动设计(DDD)的核心步骤,它通过将复杂的业务需求和规则转化为可管理的领域模型,使开发团队能够更好地理解和实现业务逻辑。以下是领域建模的主要步骤:
6.1 了解业务领域
- 业务调研:与业务专家、用户和相关方进行深入交流,理解业务需求、目标和痛点。
- 业务流程分析:梳理和分析业务流程,明确业务操作、角色和参与者。
6.2 提炼领域概念
- 识别实体(Entity):找出具有唯一标识和生命周期的对象。例如,订单、客户等。
- 识别值对象(Value Object):找出没有唯一标识、用来描述某些属性的对象。例如,地址、货币等。
- 识别聚合(Aggregate):将相关的实体和值对象组合成聚合,通过聚合根来管理。例如,订单聚合包括订单实体、订单项值对象等。
- 识别领域服务(Domain Service):找出跨多个实体和值对象的业务逻辑。例如,支付服务、库存检查服务等。
6.3 定义限界上下文(Bounded Context)
- 划分子域:根据业务领域和业务流程,将系统划分为多个子域,每个子域对应一个相对独立的业务模块。
- 明确上下文边界:定义每个子域的边界,明确子域之间的接口和交互方式,确保子域内部的一致性。
6.4 设计领域模型
- 创建实体和值对象:设计实体和值对象的属性和行为,确保它们能够充分表达业务逻辑。
- 定义聚合根:确定每个聚合的根实体,聚合根负责管理聚合内部的一致性。
- 设计资源库(Repository):定义资源库接口,用于管理实体的持久化和检索。
- 实现领域服务:设计领域服务的接口和实现,将复杂的业务逻辑封装在领域服务中。
- 设计领域事件(Domain Event):定义领域事件,捕捉和传递领域内的重要事件。
6.5 验证和迭代
- 与业务专家验证:与业务专家持续沟通,验证领域模型是否正确反映了业务需求。
- 迭代优化:根据反馈不断迭代和优化领域模型,确保其准确性和实用性。
6.6 实现和测试
- 代码实现:根据领域模型进行代码实现,确保每个模型元素都得到正确实现。
- 单元测试:编写单元测试,验证领域模型的各个部分是否正确工作。
- 集成测试:进行集成测试,确保各个子域和组件之间的协作正常。
6.7 总结
领域建模是一个持续迭代的过程,贯穿于项目的整个生命周期。通过领域建模,可以将复杂的业务需求转化为清晰的领域模型,提高系统的可理解性、可维护性和可扩展性。以下是领域建模的主要步骤总结:
- 了解业务领域:深入理解业务需求和流程。
- 提炼领域概念:识别实体、值对象、聚合、领域服务等。
- 定义限界上下文:划分子域,明确上下文边界。
- 设计领域模型:创建和设计实体、值对象、聚合根、资源库、领域服务和领域事件。
- 验证和迭代:与业务专家验证模型,持续优化。
- 实现和测试:代码实现和单元测试,确保模型的正确性。
开源项目推荐
GitHub - lml200701158/ddd-framework: DDD框架
GitHub - Air433/dddbook: spring boot 领域驱动设计Demo
GitHub - EalenXie/spring-microservice-ddd: 微服务+DDD代码结构例子
GitHub - louyanfeng25/ddd-demo: MVC【以mybatis-plus为orm框架】迁移至DDD的demo
GitHub - dddplus/dddplus-demo: ♨️ Demonstrate how to use DDDplus to build a complex OMS.演示如何使用DDDplus实现一套订单履约中台OMS
GitHub - xlorne/springboot-ddd-demo: 如何做好一个项目?springboot与领域模型该如何结合?