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(上下文映射)
image-20210311142539037
  • 继承方式
    • RPC
    • Restful等api
    • 消息队列
  • 种类
    • 共享内核-Shared Kernel
    • 客户/供应商-Customer/Supplier
    • 追随者-Conformist
    • 防腐层-Anticorruption Layer
    • 公开主机服务-Open Host Service
    • 各行其道 - Separate Way
    • 合作关系 - Partnership

设计方法

分层架构
image-20210311111555717
  • 基本原则:每层只能和位于下方的层产生耦合
    • 严格分层架构:每层只能与直接位于下方的层发生耦合
    • 松散分层架构:任意上层和任意下层发生耦合
    • 可以通过观察者等模式,让下层和上层发生耦合
六边形架构
image-20210311111942873
  • 六边形架构 其实是分层架构的一种扩展,是原来分层架构的另外一种解读,是一种端口+适配器风格的架构
  • 通过端口+适配器将领域包裹起来, 形成清晰的应用程序边界
SOA
  • SOA原则
    • 服务契约
    • 松耦合
    • 服务抽象
    • 服务重用性
    • 服务自治性
    • 服务无状态性
    • 服务可发现性
    • 服务组合性
  • SOA 精神所在
    • 业务价值高于技术策略
    • 战略目标高于项目利益
RESTful
  • 将RESTful和DDD结合的2种方式
    • 为系统接口层单独创建一个限界上下文
    • 使用标准媒体类型的时候,如果某种媒体类型不用于支持单个系统接口,我们可以可以创建一个领域模型来处理。
CQRS
事件驱动

ContextMapper

image-20210315200928557
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
image-20210315201429551
  • Subdomain 类型
    • CORE_DOMAIN
    • SUPPORTING_DOMAIN
    • GENERIC_SUBDOMAIN