领域驱动设计 + UML 相关思想详解笔记

领域驱动基本概述

模型关系图

实体(Entity) & 值对象(Value Object)

1
2
实体:在时间上有连续性,并且有唯一标识可以来区分的对象。
值对象:用来描述事物的,不区分谁是谁的,不可变的对象。

服务(Services)

服务存在的目的就是为领域提供简单的方法。为了提供大量便捷的方法,自然要关联许多领域模型,所以说,行为(Action)天生就应该存在于服务中。
服务具有以下特点:

  • a)服务中体现的行为一定是不属于任何实体和值对象的,但它属于领域模型的范围内
  • b)服务的行为一定设计其他多个对象
  • c)服务的操作是无状态的

工厂(Factories)

工厂专用于构造实体和聚合很复杂对象

以下场景不需要工厂:

  • a)构造器很简单
  • b)构造对象时不依赖于其他对象的创建
  • c)用策略模式就可以解决

层结构

  • User Interface(DubboProvider)

展现层:负责向用户展现信息,解析用户行为。

  • Application Layer

应用层:它主要为程序提供任务处理,没有业务逻辑代码,。

  • Domain Layer

领域层:这一层包含有关领域的信息;是业务的核心,领域模型的状态都直接或间接(持久化至数据库)存储在这一层。

  • Infrastructure Layer

为其他层提供底层依赖操作。如Mongdb,Redis,Mysql等。


公司规定

术语

  • Provider:属于应用层,提供程序,对外暴露 Dubbo Service,主要进行领域行为的编排工作。
  • QueryProvider:属于应用层,提供程序,对外暴露 Dubbo Service,主要进行 QueryRepository 的编排工作。
  • QueryRepository:属于应用层,为单纯的 Provider 查询接口提供技术支撑。
  • DO:属于领域层。领域对象实体,封装内聚业务实体属性和行为。
  • VO:属于领域层。领域对象值对象,封装 DO 中的整体不变性属性。
  • Factory:属于领域层。工厂,封装领域对象实体或值对象的创建。
  • Repository:属于领域层。资源库,提供 DO 的持久和获取。
  • Service:属于领域层。领域服务,表示一个无状态的操作,单个 DO 的行为不足以满足 Provider 行为时定义。
  • Event:属于领域层。领域事件。
  • Specification:属于领域层。约束条件,封装执行内聚业务时需要判断的业务约束。
  • Facade:属于领域层。防腐层,封装调用其他业务域的 Provider 或 QueryProvider 的行为。

开发规则说明

通过特定的语言开发,一定会有各种特定的问题,所以需要相应的开发规范。

针对基于 Spring 的 Java 开发,规则如下:

  1. 所有 QueryProvider 查询接口不走领域层,直接调用 QueryRepository。
  2. 所有的调用非本业务域的 Provider 通过 Facade 包装。
  3. Repository 返回的 DO 是个完整的 DO。换句话说,就是可以进行接下来的业务操作。所以,如果这个 DO 的某些 VO 需要通过查询其他服务的,在 Repository 里调用 Facade 接口,封装好后再返回整个 DO。
  4. 一般情况下,一个 Repository 只有一个更新整个 DO 的方法。如果遇到性能问题,如批量更新,允许创建另外一个更新方法。
  5. 事务控制在 Repository 和 Service。
  6. 如果 DO 在执行某个方法时有需要,允许注入领域中其他接口,包括 Facade、Service、Specification。
  7. Specification 不是所有的业务规则,而是指约束条件,如:订单的商品数量不能为 0。
  8. 修改 DO 时,通过对 Repository 查询获得的 DO 修改,然后使用 Repository 保存这个 DO。不要新建一个新的 DO。
  9. Repository 实现包中的 Convertor 一定是做 PO 相关的转换。换句话说,就是入参是 PO 或者返回值是 PO。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Client:
Constant: 封装常量
Param: 参数DTO封装
Result: 结果DTO封装
Provider:
Provider: Dubbo服务暴露层
QueryProvider: Dubbo查询服务暴露层
Service:
Application:
Provider:
ProviderImpl: Dubbo服务暴露实现层
QueryProviderImpl: Dubbo查询服务暴露实现层
Repository:
QueryReposity: 查询持久
Domain:
Factory: 主要作convert转换操作,DTO与DO的转换成
Model: 各类数据库DO
Repository: 再次丰富持久层
Reposityry: 操作持久
Service: 复杂操作多个Repository,放入接口与实现
Impl: 服务实现层
Specification: 约束条件
Infrastructure:
Exception: 封装ISP、ISV异常机制
Facade: 定义接口与实现类,操作复杂的外部Provider
Repository: 实现与redis和mysql操作的持久实现类
Converter: PO与DO的转换层
Entity: PO实体类
mapper: Mybatis的mapper操作类
Impl: Repository的操作实现层
mongo: Mongo数据操作层
Util: 工具包

Provider、QueryProvider:可以直接操作Repository,如果遇到多个复杂的Respository以操作Service为准

Facade:只操作Reference的外部Provider

Factory:只负责进行Convert转换操作。

领域驱动设计思想

架构设计面向接口开发、还是面向技术or业务开发?

许多技术人眼中,架构设计等同于技术架构设计,对架构师理解也只是关注各类技术体系和框架的掌握程度

然而很多团队并不缺这类人,但是产品开发还是很失败, 主要是对系统设计,业务理解和规划缺少足够的重视

面对复杂的业务逻辑架构师会采用主流架构设计理念和对业务充分理解并进行整合

领域驱动设计核心

  • 设计的策略维度

关注如何设计领域模型及对领域模型的划分,目的在于清楚划分系统和业务关注点

  • 设计的技术维度

关注技术实现,指导如何具体实施领域驱动

面向领域的策略设计

  • 用于抽象业务模型的领域或子域
  • 用于业务提供统一认识的通用语言
  • 用于划分系统边界并考虑系统集成的界限上下文

通用语言

通用语言解决业务人员和技术人员协作过程中非常重要的问题
思路是面向领域和业务, 统一团队成员对领域知识的一致认识, 促进后续代码模型中的命名等使用领域词汇而不是技术词汇

领域与上下文

任何系统都是从简单到复杂, 从集中到分散
由于分工模糊和业务复杂的上升,系统架构逐渐被腐化, 直到系统不能承受任何改变, 也就到了需要重新拆分的阶段

系统拆分策略

根据子域和界限上下文概念就可以对系统进行拆分
上下文的构建很重要, 其他的比如根据开发任务分配一个团队负责一个上下文, 这类往往违背业务架构驱动技术架构的原则, 在不断的系统修改中腐化架构
在架构的演进和腐化结果的表现之一就是所谓的大泥球风格, 这种风格实际上是一种没有清晰结构的风格
大泥球基本是边界模糊,无法把握系统之间如何进行整合和集成的过程,拆分子域同样也要考虑如何把拆分完的子域有效整合起来,避免子域演变成大泥球

系统拆分的方法

  • 如何找到拆分的切入点?
    领域驱动给到了子域的概念, 子域作为系统拆分的切入点,其来源往往取决于系统的特征和拆分的需求, 如核心功能、辅助性功能、第三方功能等
  • 如何对拆分后的功能进行组装?
    思路是系统集成,即子域之间通过有效的集成整合到一起构成一个大的业务功能,子域集成的策略和技术体系被称为界限上下文
子域

通过对子域抽象,梳理出通用的分类方法, 各个子域可分为核心域、支撑子域、通用子域三种类型

核心业务为核心域、专注业务的某一方面为支撑子域、可以用于整个业务系统且作为基础设施功能的归位通用子域

界限上下文

界限指某个模型概念、属性和操作

A上下文和B上下文都存在User对象,但B上下文中的User对象不同于A上下文中的User对象
而B上下文中的Account对象可能基于A上下文中的Role对象, 这个时候界限的划定能在很大程序上影响系统的设计和实现。

根据以上会把User作为核心域、Account和Role作为支撑子域

系统上下文集成策略

集成模式思路在于两点, 一点是解耦 另一点是统一
解耦:关注集成的实现与业务逻辑的实现相分离
统一:在于上下文定义协议和通过协议访问,集成模式基本是防腐层(AcL)和统一协议(UP)

防腐层:根据下游上下文领域模型单独创建一层,该层完成与上游上下文之间的交互,从而实现隔离业务逻辑,实现解耦
统一协议:提供一致的协议规定,促进其他上下文通过协议访问

显然防腐层模式面向下游上下文,统一协议面向上游上下文

领域驱动架构风格

领域核心组件

  • 领域组件(Domain): 代表整个领域驱动设计的核心, 包括对领域、子域、界限上下文等策略设计相关内容, 领域组件代表抽象模型、并不包含具体实现细节和技术
  • 基础设施(Infrastructture): 数据持久化的具体实现, 通用的工具类服务等
  • 应用组件(Applicaton): 对领域组件的一种封装, 作为网关对外提供的统一访问入口

领域组件作为核心组件和其他组件之间的依赖关系时怎样的?
领域组件的抽象接口谁去实现?

以上两个问题需要找到合理的组件划分和分层结构,需要理解分包原则在领域驱动设计中的应用

分包原则

分包三条原则

  • 无环依赖原则: 不出现环路调用,通过上移和下移或回调等手段打破循坏依赖
  • 稳定依赖原则: 认为被依赖者应该比依赖者更稳定
  • 稳定抽象原则: 抽象程度应该与其稳定程度保持一致
    一个稳定的组件也是抽象的,这样稳定性不会无法扩张,另一方面一个不稳定的组件也应该是具体的,不稳定性使其内部代码便于修改