领域驱动设计-领域建模
创始人
2024-02-29 06:28:07
0

领域建模

领域建模是针对问题空间的战术求解的过程:观察真实世界的业务需求,对业务知识进行提炼和转换,排除技术因素对建模产生的影响,一切围绕着业务需求而来。同时满足未来的需求变更与产品维护

快速建模法:名次动词法

建模过程

  1. 名词建模
    1. 识别业务流程(用例)中的名词
  2. 动词建模
    1. 识别动词,判断对应的行为是否产生了过程数据(补充手段,看是否产生了影响管理、法律或财务的过程数据,如果缺少它的记录,就会影响到商业的运营管理、造成经济损失或引起法律纠纷)
  3. 归纳抽象
  4. 确定关系
    1. 确定领域概念之间的关系

名词建模

只要名词属于领域概念,符合统一语言的要求,就快速将他提取出来,放到领域分析模型中

动词建模

讲识别出来的动词当作一个领域行为,然后看他是否产生了影响管理、法律或财务的过程数据。

  1. 驱动出隐藏的关键概念:针对动词代表的领域行为,是否需要记录过程数据
  2. 验证挖掘出来的业务概念是否真的属于领域分析建模的核心概念:如果缺少了过程数据,是否影响运营管理、引起法律纠纷或造成经济损失

归纳抽象

为了提高模型的质量,可对已有领域概念进行归纳抽象,主要是针对由那些定语修饰的领域概念。如配送地址、家庭地址、已付款金额、冻结资金等。需要分辨他们是类型的差异还是值的差异,如果是值的差异,类型相同,应归为一个领域概念。

  • 比如收获地址和家庭地址表达了不同的值,但实际上都是地址Address类型
  • 订单状态和商品状态修饰的都是状态,但实际上代表完全不同的值(类型),两个概念不能合并

注意:在分析阶段,如果分不清楚一个模型应该保留还是删除时,应优先考虑保留,待到领域姜末设计时在进行判断

确定关系

如果某个类型拥有多种相似的关联,可以为这些关联对象定义一个新的类型。也就是说如果发现用一个领域概念来描述关系更为合理,就可以将该关系建模为一个领域概念。比如:读者和作平之间存在关联关系,表达了一种收藏的概念,故可以提炼出收藏的概念

建模书籍:《分析建模:可复用的对象模型》,《彩色UML建模》


领域模型设计要素

  1. 实体:谓语描述的主体
  2. 值对象:为主体对象的属性
  3. 领域事件:封装了主体的状态
  4. 领域服务

1、实体

巴门尼德认为实体是不同变化状态的主体:主体的状态在相当长一段时间内会持续的变化,因此需要一个身份标识来标记。一个实体应具备3个要素:

  1. 身份标识
  2. 属性
  3. 领域行为

身份标识

身份标识是实体对象的必要标识,在领域驱动设计中,没有身份标识的领域对象就不是实体。身份标识的主要目的是管理实体的生命周期

属性

实体的属性用来说明主体的静态特征,可分为:

  • 原子属性:不可再分。【基本类型】
  • 组合属性

如何定义属性是原子属性和组合属性?

  • 划分标准:该属性是否存在约束规则、组合因子或属于自己的领域行为
  • 约束规则:即为业务规则
  • 组合因子:是否不可再分。比如重量、体积,需要与计数单位共同组合,如果只有值而无单位,就会因为单位不同导致计算错误
  • 领域行为:每个抽象层只专注于做自己的事情,各司其职,这样实体类就能分配职责

领域行为

领域行为:可以更好的说明其作为主体的动态特征

  1. 变更状态的领域行为:应该让变更状态的方法名满足业务含义
  2. 自给自足的领域行为:意味着实体对象只操作了自己的属性
  3. 互为协作的领域行为:通过方法参数传入

实体拥有的变更状态的领域行为,修改的只是对象的内存状态,与持久化无关

2、值对象

值对象是不可变的,不需要分配标识。实体与值对象的本质区别在于是否拥有唯一的身份标识

值对象和实体的区别

  • 业务对它相等的判断是:依据值还是依据身份标识
  • 确定属性值是否会发生变化:如果变化了,是产生一个完全不同的对象,还是维持相同的身份标识。前者是值对象,后者是实体
  • 生命周期的管理:值对象没有生命周期的管理

不变性

领域驱动设计建议尽量将值对象设计为不变类,因为一个不变的类是线程安全的。如果既要保证对象的不变性,又要满足更新状态的需求,就需要用一个保存了新状态的实例来替换原有的不可变对象

值对象的优势

  1. 内建类型无法展示领域概念,比如String 和 Name
  2. 内建类型无法封装领域逻辑
  3. 内建类型缺乏验证能力

3、聚合

类的关系

  1. 泛化: 子类继承父类
  2. 关联:代表整体的对象包含了代表部分的对象,即为组合关系
    1. 合成:体现了强烈的所有权的特征,即组合关系的两个对象属于同一个生命周期。比如学校和教室
    2. 聚合:没有所有权特征,不会约束它们的生命周期。比如教室和学生
  3. 依赖:一个类使用了另一个类的信息或服务

引入边界:聚合

当规模越来越大时,类之间的关系变得错综复杂,对象的层次变得越来越深,类之间的关系难以梳理和控制。因此需要引入边界来降低和限制领域类之间的关系,每个边界都有一个主对象作为外交发言人,总体负责与外部的协作。

  • 就好像公司员工多了之后,会分部门,部门下会分团队,便于管理

引入这种关系后,就可以只保留主对象之间的关系。这种层次的边界称为聚合,边界内的主对象称为聚合根。聚合在限界上下文与类的粒度之间形成了中间粒度的封装层次

聚合的特征

  • 聚合是包含了实体和值对象的一个边界
  • 聚合内包含的实体和值对象形成一棵树,只有实体才能作为这颗树的根:因为聚合需要通过资源库管理生命周期,要管理生命周期,就需要通过身份标识对其进行跟踪
  • 外部对象只允许持有聚合根的引用
  • 聚合内部需要保证事务的一致性
  • 由聚合根统一对外提供履行该领域概念职责的行为方法,实现内部各个对象之间的协作

聚合的设计原则

**当聚合边界存在模糊时,独立性对聚合边界的影响要高于完整性。**完整性将聚合视为一个高内聚的整体,独立性影响了聚合的粒度,不变量时对动态关系的业务约束,一致性体现了聚合数据操作的不可分割

  • 完整性
    • 对内、对外有一致的生命周期
  • 独立性
    • 需要看待合并实体是否会被调用者单独使用,比如汽车和发动机。汽车没有发动机不完整,但是发动机可以单独使用
    • 当聚合边界存在模糊时,独立性对聚合边界的影响要高于完整性
  • 不变量
    • 聚合内部的恒定关系,可以理解为固定规则
  • 一致性:事务的一致性
    • 原子性:聚合不可再分的领域概念
    • 一致性:聚合边界内最重要的不变量就是一致性约束
    • 隔离性:通过唯一的身份标识进行聚合关联
    • 持久性:一个聚合只有一个资源库,由资源库保证聚合整体的持久化

最高原则

只有聚合根才是访问聚合边界的唯一入口,聚合外部的对象不能引用除根实体之外的任何内部对象。聚合之间通过身份标识进行引用

4、聚合生命周期的管理

生命周期经历的各种状态取决于存储介质,内存与硬盘,分别对应对象的实例化与数据的持久化。如果不是因为计算机无法做到永不宕机且内存资源便宜,那么是可以不进行持久到外部存储设备中。

在领域模型的设计要素中,由聚合根实体的构造函数或者工厂负责聚合的创建,若要修改聚合的状态,需要在内存中先进行状态的变更,然后通过持久化确保聚合对象与数据记录的一致

工厂

**工厂封装了聚合对象的创建逻辑。**许多面向对象与药支持类通过构造函数创建自己,对象自己创建自己,就好像扯着自己的头发离开地球表面,不合情理。

  • 由被依赖聚合担任工厂
  • 引入专门的聚合工厂
  • 聚合自身担任工厂
  • 消息契约模型或装配器担任工厂
  • 使用构建者组装聚合

资源库

资源库是对数据访问的一种业务抽象,分离了聚合的领域行为和持久化行为,保证了领域模型对象的业务纯粹性。

一个聚合一个资源库,如果要访问聚合内的非根实体,需要通过聚合访问。在资源库获得整个聚合后,将根实体作为入口,在内存中访问封装在聚合边界内的非根实体对象

  1. 资源库和数据访问对象(DAO)的区别?
    • 数据访问对象在访问数据时,无聚合的概念,可以针对领域层的任何模型对象
  2. 如何设计资源库接口?
    1. 一派认为以通用性换取接口的可扩展,但却牺牲了接口方法的可读性
    2. 一派以封装获得接口的可读性,却因为方法过于具体导致接口膨胀与不稳定

可以一边为资源库定义常见的个性化查询方法,一边保留对查询条件的支持。因为一旦资源库提供了通用的查询接口,就会将组装查询条件的代码混入应用层,违背了保持应用层轻薄的原则

5、领域服务

聚合的已知数据不一定满足完整的领域需求,为了保证聚合的自治性,需要将不足的部分作为方法的参数传入。但两个聚合之间的协作应该由谁负责发起呢?——由领域服务负责

运用场景

  1. 当针对领域行为建模时,优先考虑使用值对象和实体来封装领域行为。如果领域行为的变化方向没有拥有数据的类保持一致,就应分离变与不变,将这一变化的领域行为从所属的聚合中剥离出来,形成领域服务
  2. 当有两个聚合需要进行协作时,该由领域服务负责
  3. 当业务需要调用其他服务时,也应该由领域服务负责,不应该在聚合内部引入对南向网关端口的依赖

归纳为:

  1. 与状态无关的领域行为
  2. 变化方向与聚合不一致的领域行为
  3. 聚合之间协作的领域行为
  4. 聚合和端口之间协作的领域行为

6、领域事件

特征

  1. 领域事件代表了领域概念
  2. 领域事件是已经发生的事实
  3. 领域事件是不可变的领域对象
  4. 领域事件会基于某个条件而触发

定义

  1. 事件ID:通过ID唯一标识事件,进行管理
  2. 事件发生时间

领域设计建模

实体、值对象与领域事件共同构成了描述真实世界业务问题的基本要素;聚合从设计角度为实体与值对象圈定了概念边界,并映入了工厂和资源库的设计模式,用于管理聚合的生命周期;领域服务作为聚合的补充,专注于领域行为的表达,负责协调聚合之间以及聚合与端口之间的协作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qy7CBxRE-1669707561663)(images/Z0bukJd4bYhhjqw6O-QoF60UclBc8ae9cn3TZJi8c3k.png)]

  • 远程服务:若为当前限界上下文的远程服务,负责响应角色的服务请求
  • 应用服务:提供具有服务价值的服务接口,完成消息契约对象与领域模型对象的转换,调用货编排领域服务
  • 领域服务:提供聚合无法完成的业务功能,协调多个聚合以及聚合与端口之间的协作。封装领域逻辑,以避免其泄漏到应用层
  • 聚合:作为信息的持有者,履行自给自足的领域行为
  • 工厂:封装复杂货可能变化的创建聚合的逻辑
  • 端口:作为访问外部资源的抽象
  • 适配器:端口的实现,提供访问外部资源的具体技术实现,并通过依赖注入设置到领域服务或应用服务中

设计聚合

  1. 理顺对象图:辨别实体还是值对象
  2. 分解关系薄弱处:以关系强弱为界,以聚合边界为刀,逐一分解
  3. 调整聚合边界

分解关系

泛化关系的处理

  • 整体视角:调用者不关心特化的子类之间的差异
  • 独立视角:调用者只关注具体的特化子类,此时应以特化的子类作为独立的聚合根

如果一个继承体系的子类存在不同于父类和其他子类的特定属性,说明该子类具有了领域概念的独立性。

如果是合成关系,也属于一个聚合

服务驱动设计

在这里插入图片描述

  1. 分解任务:根据职责的层次对业务服务进行任务分解,直到分解为原子任务
    1. 同一层次的任务必须位于同一个抽象层次
  2. 分配职责:为角色构造分配不同层次的职责

分解任务

  1. 把基本流程以动词短语形式列出,作为基础任务
  2. 以归纳法将具有相同目标的基础任务由上而下归纳为组合任务
    1. 比如验证订单有效性和验证库存有效性具有共同的目标,就是验证订单可以归纳为一个组合任务
  3. 再以分解法判断基础任务是否是原子任务,如果不是,就自上而下进行拆分

分配职责

  1. 远程服务:匹配业务服务
  2. 应用服务:匹配业务服务。自身并不包含任务领域逻辑,仅负责协调领域模型对象,通过它们的领域能力组合完成一个完整的应用目标,完成对领域服务和聚合的协调
  3. 领域服务:匹配组合任务,领域服务的主要目的就是控制多个聚合与端口之间的协作
  4. 聚合:匹配原子任务
  5. 端口:匹配原子任务,抽象对外部资源的访问

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...