DDD 领域建模笔记
核心概念
战术设计(Tactic DDD): Entity, Value Object; Aggregate, Root Entity, Service, Domain Event; Factory, Repository
战略设计(Strategic DDD): Bounded Context, Context Map; Published Language, Shared Kernel, Open Host Service, Customer-Supplier, Conformist, Anti Corruption Layer (context relationship types)
Entity(实体)
- 存在 2 点特征
- 唯一标志:当一些对象不是由属性定义,而是由一个唯一标志定义的话,我们就可以认为它是一个实体。
- 连续性:对象的连续性体现在对象是有生命周期的。
- 由上2点可以看出,实体并非一定是映射到我们现实世界的某个具体事物
- 生成实体唯一标识的 4 种方法
- 用户提供一个或者多个初始唯一值作为输入时
- 程序内部通过某种算法自动生成身份标识,例如UUID、雪花ID等
- 程序依赖于持久化存储,比如数据库生成的自增主键
- 通过其他的限界上下文决定出的唯一标识,作为程序的输入。
- 实体不变性
- 一个实体维护了一个或者多个不变条件
- 不变条件主要是由聚合所关注
Value Object(值对象)
- 当我们只关心一个模型元素的属性时,应把它归类为值对象。
- 它度量或者描述了领域中的一件东西。
- 它将不同的相关属性组合成了一个概念整体。
- 它可以和其他值对象进行相等性比较。
- 值对象应该是不可变的,不要为它分配任何标识,不要将它设计得跟实体一样复杂。
- 值对象应该具有无副作用性
Aggregate(聚合)& Root Entity(根对象)
- 描述
- 每个聚合都有一个根和一个边界,边界内定义了聚合的内部有什么。”根” 是聚合所包含的一个特定的实体。
- 外部对象可以引用根,但不能引用聚合内部的其他对象,聚合内的对象之间可以相互引用,除了根实体外,其他实体拥有本地标识。
- 定义
- 我们应该将实体和值对象分门别类的聚集到聚合当中,并定义聚合的边界。并通过根来控制边界内其他对象的所有访问。只允许外部对象保持对根的引用。对内部成员的临时引用可以被传递出去,但仅在一次操作中有效。
- 不变性和一致性边界即是聚合的设计依据和精髓。
- 这里的不变性指的是业务规则,该规则应该始终保持一致
- 一致性边界的意思是单个事务的修改范围。 原则上我们应该在一个事务里只修改一个聚合。
- 聚合的作用
- 为了维护对象生命周期内的完整性
- 通过定义清晰的所属关系和边界,在这个边界中的模型元素在生命周期内必须维护一致性,通俗的讲就是业务规则。
- 聚合特征
- 根实体具有全局的标识,它最终负责检查固定规则。
- 边界内的实体具有本地标识,这些标识只在聚合内部才是唯一的。
- 聚合外部的对象不能引用根实体之外的聚合内部对象。根实体可以将内部实体的引用传递给它们,但只能临时使用。或者传递一个值对象的副本出去,而不用关心它发生了什么变化。
- 只有根实体才能直接通过数据库直接查询,其他对象必须通过遍历关联来发现。
- 根实体可以保持其他根实体的引用。
- 当对聚合边界内的任何对象做了修改时,整个聚合的所有固定规则都必须被满足。
- 原则
- 通过唯一标识去引用其他聚合
- 引用聚合和被引用的聚合不可以在同一个事务中进行修改
- 如果你在试图在单个事务中修改多个聚合,这往往意味着此时的一致性边界是错误的,发生这样的情况通常是我们遗留了某些建模点,或者尚未发现通用语言中的某个概念。
- 当试图修改多个聚合的话,我们也应该采用最终一致性而非原子一致性。
- 利用应用层来处理聚合内的依赖关系,避免在聚合中使用资源库或者领域服务。
- 边界之外使用最终一致性
- 通过唯一标识去引用其他聚合
Service(领域服务)
- 三个特征
- 它是与领域相关的操作如执行一个显著的业务操作过程,但它又并不适合放入实体与值对象中。
- 操作是无状态的。
- 对领域对象进行转换,或以多个领域对象作为输入进行计算,结果产生一个值对象。
- 区分不同的服务
- 应用服务:获取输入,发送消息给领域层,监听确认消息,决定使用基础服务来发送邮件。
- 领域服务:协调账户模型和总账模型进行交互,执行相应的领域行为。
- 基础服务:按照应用服务的指示发送邮件。
- 粒度
- 我们应该尽量避免领域知识泄露到应用层当中去。那此时领域服务就不失为一种良好的处理方式,通过将细粒度的领域对象封装到领域服务当中去,将领域知识限制在领域服务当中,形成粗粒度的领域对象。
转换过程
Domain Event(领域事件)
领域专家所关心的发生在领域中的一些事件。将领域中所发生的活动建模成一系列的离散事件。每个事件都用领域对象来表示…领域事件是领域模型的组成部分,表示领域中所发生的事情。
一个领域事件必须对业务有价值,有助于形成完整的业务闭环,也即一个领域事件将导致进一步的业务操作。
事件风暴
事件风暴是一项团队活动,旨在通过领域事件识别出聚合根,进而划分微服务的限界上下文。
在活动中,团队先通过头脑风暴的形式罗列出领域中所有的领域事件,整合之后形成最终的领域事件集合,然后对于每一个事件,标注出导致该事件的命令(Command),再然后为每个事件标注出命令发起方的角色,命令可以是用户发起,也可以是第三方系统调用或者是定时器触发等。最后对事件进行分类整理出聚合根以及限界上下文。
Factory
- why
- 隐藏创建的细节
- 对内,复杂对象除了本身生命周期的维护外,如果再承担自身的创建,会导致负载过重
- 对外,不需要客户理解对象创建过程
- 定义
- 复杂对象的创建是领域层的职责,但这项任务并不一定属于那些用于表示模型的对象,他们没有对应模型中的事物,但又确实承担了领域层的职责。
- 应该将创建复杂对象和聚合的职责转移给单独的对象,这个对象本身可能没有承担领域模型中的职责,但它仍然领域设计的一部分。
- 在创建聚合时要把它作为一个整体,并确保它满足固定规则。
- 设计要点
- 每个创建方法都应该是原子的,并保证生成的对象处于一致的状态。
- 可以使用独立的工厂或者在聚合根上使用工厂方法。
- 工厂方法的参数应该是较低层的对象。
- 非必要场景,直接构造函数即可
- 类仅仅是一种类型,没有其他子类,没有实现多态性。
- 客户关心的是实现类。
- 客户可以访问对象的所有属性,因此向客户公开的构造函数中没有嵌套的对象创建。
- 构造过程很简单。
- 公共构造函数必须遵守与工厂相同的规则,必须是原子操作且满足所有固定规则。
- 不要在构造函数中调用其他构造函数,应保持构造函数的简单。
Repository
- 实现
- 对类型抽象
- 充分利用与客户进行解藕
- 事务的控制权交给客户
- 优点
- 为客户提供了一个简单的模型,可用来获取持久化对象并管理他们的生命周期
- 他们将应用程序和领域设计与持久化技术进行解耦
- 它们体现了有关对象访问的设计决策
- 很容易测试,将利用集合直接替换资源库进行测试
Bounded Context (限定上下文)
- 领域、子域、限界上下文
- 领域即是一个组织所做的事情以及其中所包含的一切
- 模型只在限界上下文中变动,不影响其他限界上下文,将变动的影响范围控制在单个限界上下文中
- 一般来说,一个子域对应一个限界上下文,但是子域并不一定与限界上下文一一对应
- 领域种类划分
- 核心域:公司主要的业务领域,比如生鲜的商品子域以及订单子域(核心域并不绝对)
- 支撑子域:公司的库存帮助公司完成销售。他们就属于支撑子域。
- 通用子域:会员子域,在许多的网上购物平台上都会使用到的会员体系。它属于通用子域。
- 限界上下文是一个显式的边界,领域存在于这个边界之内。
Context Map(上下文映射)
- 继承方式
- RPC
- Restful等api
- 消息队列
- 种类
- 共享内核-Shared Kernel
- 客户/供应商-Customer/Supplier
- 追随者-Conformist
- 防腐层-Anticorruption Layer
- 公开主机服务-Open Host Service
- 各行其道 - Separate Way
- 合作关系 - Partnership
设计方法
分层架构
- 基本原则:每层只能和位于下方的层产生耦合
- 严格分层架构:每层只能与直接位于下方的层发生耦合
- 松散分层架构:任意上层和任意下层发生耦合
- 可以通过观察者等模式,让下层和上层发生耦合
六边形架构
- 六边形架构 其实是分层架构的一种扩展,是原来分层架构的另外一种解读,是一种端口+适配器风格的架构
- 通过端口+适配器将领域包裹起来, 形成清晰的应用程序边界
SOA
- SOA原则
- 服务契约
- 松耦合
- 服务抽象
- 服务重用性
- 服务自治性
- 服务无状态性
- 服务可发现性
- 服务组合性
- SOA 精神所在
- 业务价值高于技术策略
- 战略目标高于项目利益
RESTful
- 将RESTful和DDD结合的2种方式
- 为系统接口层单独创建一个限界上下文
- 使用标准媒体类型的时候,如果某种媒体类型不用于支持单个系统接口,我们可以可以创建一个领域模型来处理。
CQRS
事件驱动
ContextMapper
ContextMap
- contextMap 只有2种type
- SYSTEM_LANDSCAPE 这个是从系统层面描述 ContextMap
- ORGANIZATIONAL 这个是从组织团队层面描述 ContextMap
BoundedContext
- 一个BoundedContext 可以实现多个domain
- 一个BoundedContext 可以 refines 另外一个
- BoundedContext 类型有4种
- FEATURE
- APPLICATION
- SYSTEM
- TEAM
- Knowage level 有2个
- CONCRETE
- META
Domain and Subdomain
- Subdomain 类型
- CORE_DOMAIN
- SUPPORTING_DOMAIN
- GENERIC_SUBDOMAIN