type
status
date
slug
summary
tags
category
icon
password
AI summary
Last edited time
Feb 21, 2025 03:38 AM
一些概念的理解
界限上下文

- 定义清晰的边界:
每个界限上下文都拥有自己的一套业务逻辑、实体、用语和规则。例如,一个“客户管理”系统和一个“订单管理”系统可能都涉及“客户”这一概念,但它们在各自的界限上下文中对“客户”的定义和处理方式可能不同。
- 不同子域的隔离:
在DDD中,系统通常分为多个子域(Subdomains)。这些子域可能会有不同的需求、不同的模型、不同的业务规则。界限上下文明确了每个子域的业务范围和边界,避免不同子域之间的混淆和交叉干扰。
- 上下文内的统一语言:
在一个界限上下文内,团队会使用统一的术语和语言来确保所有成员对同一概念有共同的理解。这就是DDD中提到的领域语言(Ubiquitous Language)。例如,在“订单管理”界限上下文中,"订单"的定义、属性和行为可能与其他上下文中的“订单”定义有所不同。
- 与其他界限上下文的关系:
- 共享内核(Shared Kernel):多个上下文共享一些核心模型。
- 客户-供应商(Customer-Supplier):一个上下文作为另一个上下文的“客户”,依赖于其提供的功能。
- 防腐层(Anti-Corruption Layer):为避免不同上下文间的影响和混淆,定义一个层来隔离和转化数据。
- 发布-订阅(Published-Subscribed):一个上下文发布事件,另一个上下文订阅这些事件。
不同的界限上下文之间可能会存在交互,通常通过上下文映射(Context Mapping)来定义。常见的关系类型有:
举例

实体、值对象、聚合根、领域服务、领域事件
- 实体 当一个对象由其标识(而不是属性)区分时,这种对象称为实体(Entity)。理解上来说就是id,例如,人可以通过身份证号这种独一无二的标识来进行区分。而不是人的属性,例如身高,年龄等。
- 值对象 当一个对象用于对事物进行描述而没有唯一标识时,它被称作值对象(Value Object)。例如年龄28岁。使用值对象,可以更好的做系统优化和设计的精简优化。在实践中,需要保证值对象创建后就不能被修改,即不允许外部再修改其属性。
- 聚合根 聚合一组相关对象的集合,作为一个整体被外界访问,聚合根是这个聚合的根节点。聚合由根实体、值对象和实体构成。聚合是一个非常重要的概念,核心领域往往都需要用聚合来表达。其次,聚合在技术上有非常高的价值,可以指导详细设计。
- 领域服务 一些重要的领域行为或操作,可以归类为领域服务。它既不是实体,也不是值对象的范畴。当我们采用了微服务架构风格,一切领域逻辑的对外暴露均需要通过领域服务来进行。如原本由聚合根暴露的业务逻辑也需要依托于领域服务
- 领域事件 是表示系统中发生了某个具有业务意义的事件。它通常用于表示业务中某些状态变化或者某个重要行为的发生。这些事件通常是不可变的,一旦发生就会携带相关的业务数据,表示一个已经发生的事实。
充血模型代码举例
在电商场景中,订单聚合根
OrderAggregateRoot
往往包含一些以下实体与值对象Order
(订单)
OrderItem
(订单商品明细)
ShippingAddress
(订单收货地址)
DDD 战术上,充血模型希望通过
OrderAggregateRoot
来作为其管理的实体、值对象的管理入口示例代码如下
从上面的示例代码上,可以看出充血模型有以下特点:
- 所有写操作,都通过领域服务加载 聚合根 后,再通过 聚合根提供的修改操作来实现数据修改的逻辑,然后再通过一次 save 操作更新到数据库中
- 加载 聚合根 时,会一次性从数据库中加载相关的所有数据信息(也可以考虑延迟加载,但是代码复杂度会上升)
- 一些单元测试,可以直接通过构造 聚合根 来进行单测,可以做到和数据库不相关
DDD 中层的划分

贫血模型的实现思考
一般习惯了 MVC 开发模式的开发团队,向 DDD 充血模型转换时,会遇到比较大的困难:
- 领域模型包含了更多的业务规则、领域事件、验证逻辑等,这增加了模型的复杂性;
- 思维模式一时半会儿难以适应,可能会觉得创建和维护这些丰富的聚合根、实体和值对象变得难度加大,开发周期会拉长;
- DDD 的 data
change tracking
JPA天然支持,但是在国内 Mybatis 大流行的背景下,没有对应的主流开源实现,通常都是面向数据库编程;
所以,在项目落地时,是否可以适当妥协,继续使用贫血模型,但是,也借鉴 聚合根 的思想,精简设计我们的领域服务,减少不必要的 Service 类,例如以上例子,我们只考虑在代码中添加
OrderService
绝对不要出现
OrderItemService
、ShippingAddressService
,所有涉及到 OrderItem
、ShippingAddress
的写操作,都交由 OrderService
领域服务管理,在开发任务划分时,先讨论梳理出聚合根,再整理得到相关的操作,分配任务时,通过将聚合根来确定领域服务,实现任务功能的分配,这样每个分到任务的人,实现写业务逻辑时,能够相对独立完整的进行开发TODO
一些涉及到只读数据查询的代码落地最佳实践,例如后端管理界面一些数据的查询组合,结合 CQRS(Command Query Responsibility Segregation,命令查询职责分离),是否能够减少领域服务相关的查询逻辑代码,所有写的操作,都纳入到领域服务管理,查询相关的服务,单独构建一个服务,来管理这部分代码
那些场景适合使用DDD
业务逻辑复杂、需要深刻理解领域知识的场景,在关系数据库模型上,通常会涉及到主表、字表状态的一致性,例如上面提到的订单主表和订单项字表,反例:字典表管理就不太适合使用DDD