文章目录
DDD概述
- DDD 全程是 Domain-Driven Design,中文叫领域驱动设计,是一套应对复杂软件系统分析和设计的
面向对象建模方法论。
- 目前最佳的实践是:有 DDD 的指导,加之微服务的事件,才是完美的架构。
按照 DDD 的约束要求:
- 第一,聚合根来保证内部实体规则的正确性和数据一致性;
- 第二,外部对象只能通过 id 来引用聚合根,不能引用聚合根内部的实体;
- 第三,聚合根之间不能共享一个数据库事务,他们之间的数据一致性需要通过最终一致性来保证。
有了聚合根,再基于这些约束,未来可以根据需要,把聚合根升级为上下文,甚至拆分成微服务,都是比较容易的。
DDD 将领域层进行了细分,是 DDD 比较 MVC 框架的最大亮点。
- **
DDD 能做到这一点,主要是因为 DDD 将领域层进行了细分,
**比如说领域对象有实体、聚合,动作和操作叫做领域服务,能力叫做领域能力等。 而 MVC 架构并没有对业务元素进行细分,所有的业务都是 Service
,从而导致 Controller 层和 Service 层很难定义出技术约束,因为都是 Service,你不会知道这个 Service 是用来描述对象的还是来描述一个业务操作的。
DDD重要概念
实体对象
- 是具有唯一标识符的对象,其身份是持久且稳定的,
即使对象的某些属性发生了变化,它仍然被认为是同一个实体
。例如,一个订单实体,即使订单的金额、商品数量等属性发生变化,只要订单编号不变,它就是同一个订单。 - 通常是可变的,其属性可以在其生命周期内发生变化,但身份不变。例如,一个客户实体的姓名、电话号码等属性可能会随着客户信息的更新而改变,但客户的身份(如客户编号)保持不变。
- 相等性是基于唯一标识符来判断的。只要两个实体对象的唯一标识符相同,即使它们的其他属性值不同,也被认为是相等的。例如,两个订单实体,只要订单编号相同,就认为是同一个订单。
- 可以是聚合根,也可以是聚合内的成员实体。聚合根是聚合的入口点,负责维护聚合内的实体和值对象的完整性。例如,一个订单聚合,订单实体可以作为聚合根,订单明细实体作为聚合内的成员实体。
值对象
- 没有标识符的对象,其身份是由其属性值共同定义的。 当值对象的属性值发生变化时,它就变成了一个全新的值对象。
例如,一个地址值对象,由街道、城市、邮编等属性组成,如果其中任何一个属性改变,那么这个地址就不再是原来的地址,而是一个新的地址。
- 是不可变的,一旦创建,其属性值就不能被修改。如果需要改变值对象的某个属性值,就需要创建一个新的值对象来替代原来的值对象。例如,一个货币金额值对象,如果需要将金额从100元改为200元,就需要创建一个新的金额为200元的货币金额值对象。
- 相等性是基于属性值来判断的。只有当两个值对象的所有属性值都相同时,才认为它们是相等的。例如,两个日期值对象,只有当它们的年、月、日都相同时,才认为是同一个日期。
- 通常是聚合内的组成部分,用于丰富聚合的业务逻辑和数据结构。例如,在一个用户聚合中,用户的地址、电话号码等可以作为值对象,它们是用户实体的属性,用于描述用户的相关信息。
聚合(Aggregate)
- 聚合是一组相关联的对象,被视为一个单元进行数据修改。
聚合的目的是将多个相关的对象组合在一起,以便在业务操作中作为一个整体进行处理,从而保证数据的一致性和完整性。
例如,一个订单聚合可能包括订单实体、订单明细实体和一些相关的值对象,如地址、货币金额等。 聚合定义了一个明确的边界,这个边界内的对象被视为一个整体。在数据库操作中,这个边界内的对象通常会作为一个事务单元进行提交或回滚
。例如,当创建一个新的订单时,订单实体和订单明细实体需要一起被持久化到数据库中,如果其中一个操作失败,整个事务都会回滚,以保证数据的一致性。- **
聚合内部的对象之间可以有复杂的关联关系,如一对多、多对多等。这些关系在聚合内部被维护,以确保业务逻辑的正确性。
**例如,一个订单实体可以包含多个订单明细实体,每个订单明细实体又可以关联到不同的产品实体。
聚合根(Aggregate Root)
聚合根是聚合的入口点,是聚合中的一个特殊实体。它负责维护聚合的完整性和一致性,对外提供对聚合内部对象的访问接口。
聚合根是聚合的代表,它拥有聚合的唯一标识符,并且可以通过这个标识符来访问聚合内的其他对象。 例如,在订单聚合中,订单实体通常作为聚合根,通过订单编号可以访问订单明细和其他相关对象。- 职责:聚合根的主要职责包括:
- 维护聚合的完整性:确保聚合内的对象在业务操作中保持一致性和完整性。例如,当修改订单明细时,订单根需要确保订单的总金额等信息也相应更新。
- 提供访问接口:对外提供对聚合内部对象的访问和操作接口,但不允许外部直接访问聚合内部的非根实体。例如,可以通过订单根来添加或删除订单明细,但不能直接访问订单明细实体。
- 协调业务逻辑:协调聚合内部对象之间的业务逻辑,确保业务操作的正确性。例如,当处理订单支付时,订单根需要协调订单状态的更新、订单明细的处理等业务逻辑。
- 唯一标识符:聚合根拥有一个全局唯一的标识符,这个标识符在整个系统中是唯一的,用于唯一标识一个聚合实例。例如,订单编号就是订单聚合根的唯一标识符,通过订单编号可以找到对应的订单聚合。
示例
假设我们有一个电商系统,其中涉及到订单管理。在这个系统中,可以定义一个订单聚合,其结构如下:
- 订单聚合根(Order Aggregate Root):订单实体(Order),拥有订单编号(Order ID)作为唯一标识符。
- 聚合内的实体:订单明细实体(Order Line),每个订单明细关联到一个产品实体(Product)。
- 聚合内的值对象:地址值对象(Address),用于存储送货地址;货币金额值对象(Money),用于表示订单金额等。
在这个聚合中,订单根负责维护订单的整体状态,包括添加或删除订单明细、更新订单金额、处理订单状态变更等业务逻辑。外部系统通过订单编号访问订单聚合,所有对订单明细和相关值对象的操作都必须通过订单根来进行,以确保订单聚合的完整性和一致性。
领域
- 领域指一种特定的范围或区域。在DDD中上下文的划分完的东西叫作领域。
- 子域:在领域不断划分的过程中,领域会细分为不同的子域,子域可以根据自身重要性和功能属性划分为三类子域,它们分别是:核心域、通用域和支撑域。
- 核心域:它是业务成功的主要因素和公司的核心竞争力
- 通用域:没有太多个性化的诉求,同时被多个子域使用的通用功能子域是通用域
- 支撑域:有一种功能子域是必需的,但既不包含决定产品和公司核心竞争力的功能,也不包含通用功能的子域,就是支撑域
比如电商系统,订单就是核心领域,支付调用银行,支付宝什么的就是支撑子域,相当于我们俗称的下游,通用子域,就是一些鉴权,用户中心,每个系统都会用到,就设计成通用子域。
领域服务
- 聚合根与领域服务负责封装实现业务逻辑。领域服务负责对聚合根进行调度和封装,同时可以对外提供各种形式的服务,对于不能直接通过聚合根完成的业务操作就需要通过领域服务。
- 聚合根本身无法完全处理这个逻辑,例如支付这个步骤,订单聚合不可能支付,所以在订单聚合上架一层领域服务,在领域服务中实现支付逻辑,然后应用服务调用领域服务。
防腐层
- 防腐层就是两个系统之间加入一个中间层,隔离第三方系统的依赖,对第三方系统进行通讯转换和语义隔离。
中间层类似适配器模式
,解决接口差异的对接,防腐层强调两个子系统语义解耦,接口转换是双向的。 - 防腐层作用
- 使两方的系统解耦,隔离双方变更的影响,允许双方独立演进;
- 防腐层允许其它的外部系统能够在不改变现有系统的领域层的前提下,与该系统实现无缝集成,从而降低系统集成的开发工作量。
资源库
资源库可以理解成 DAO,但它比 DAO 更宽泛,存储的手段可以是多样化的,常见的无非是数据库、分布式缓存、本地缓存等。资源库(Repository)的作用,就是对领域的存储和访问进行统一管理的对象。
- 资源库对外的整体访问由 Repository 提供,它聚合了各个资源库的数据信息,同时也承担了资源存储的逻辑(例如缓存更新机制等)。
- 在资源库中,我们屏蔽了对底层的直接访问,而是仅对聚合根进行资源管理。
贫血模型
- 贫血模型中的实体类就类似MVC中的model层,实体类中只定义了属性和setter getter方法。
存在的问题就是通过pojo这个对象上看不出业务有哪些逻辑
,一个pojo可能被多个模块调用,只能去上层各种各样的service来调用,这样以后当梳理这个实体有什么业务,只能一层一层去搜service,也就是贫血失忆症,不够面向对象。
充血模型
除了属性和setter getter方法,还有一系列相关的业务方法
,例如user用户有改密码,改手机号,修改登录失败次数等操作,都内聚在这个user实体中。这才是面向对象的本质,通过实体就能看出有哪些业务存在。
DDD项目结构示例
项目结构概述
- 在DDD(领域驱动设计)中,通常将项目结构划分为几个核心模块,每个模块负责不同的职责。根据您提供的图示,项目结构可以概括为以下几个主要部分:
- domain(领域层):包含核心业务逻辑和领域模型。
- model(领域模型):定义了领域中的实体、值对象、聚合和聚合根等概念。
- repo(资源库):负责数据的持久化操作,通常与数据库交互。
- service(聚合根服务层):
仅涉及单个聚合根的操作可以在聚合根服务中定义
。
- service(领域服务层):
领域服务负责协调不同聚合根之间的交互,实现更复杂的业务逻辑
。 - facade(外观层/防腐层):为外部系统提供统一的接口,隐藏内部复杂性。
- utils(工具类):提供通用的工具方法和辅助功能。
- web:处理HTTP请求,通常包含控制器和视图等Web层相关代码。
领域模型详解
- 在领域模型(model)部分,对象主要分为两大类:
- 实体(Entity):
- 实体是具有唯一标识的对象,其生命周期内标识保持不变,即使其属性值发生变化。
- 实体通常用于表示业务中的核心概念,如用户、订单等。
- 值对象(Value Object):
- 值对象是通过其属性值来识别的对象,没有唯一标识。
- 值对象通常用于描述实体的属性或状态,如地址、颜色等。
聚合和聚合根聚合(Aggregate)
- 聚合:
- 是一组相关联的对象(实体和值对象)的集合,它们共同构成一个完整的业务概念。
- 聚合内的实体和值对象之间存在紧密的关联关系,通常在一个事务中一起操作。
- 聚合根(Aggregate Root):
- 聚合根是聚合的入口点,负责维护聚合的完整性和一致性。
- 外部对象只能通过聚合根来访问和操作聚合内的其他对象,聚合根对外隐藏了聚合内部的复杂性。
服务层
- 聚合根服务:
- 仅涉及单个聚合根的操作可以在聚合根服务中定义。
- 聚合根服务负责协调聚合内的对象,实现聚合的业务逻辑。
- 领域服务:
- 当业务逻辑需要跨越多个聚合根时,需要在领域服务中定义。
- 领域服务负责协调不同聚合根之间的交互,实现更复杂的业务逻辑。