领域驱动设计,又称“软件核心复杂性应对之道”。是一套基于对象思维的业务建模设计思想,相对于 CRUD 系统有更高的灵活性,是业务人员处理复杂问题的有效手段。
领域驱动设计,整体包括战略和战术两部分,其中战略部分的落地需要团队合作、开发过程、流程制度等一系列支持,实施阻力相对较大。相反,战术部分,是一组面向业务的设计模式,是基于技术的一种思维方式,相对开发人员来说比较接地气,是提升个人格局比较好的切入点。
本 Chat 为战术模式预览,对战术相关模式进行简单介绍,其中包括:
- 实体
- 值对象
- 领域服务
- 模块
- 聚合
- 工厂
- 仓库
- 领域事件
- 事件溯源
面向人群: 厌倦 CRUD,寻求更高发展的业务开发人员
战术模式包含若干构造块模式,以便能够构建有效的领域模型。
战术模式严重依赖于领域模型和通用语言,通过技术模式将领域模型和通用语言中的概念映射到代码实现中。随着模型的进化,代码实现也会进行重构,以更好的体现模型概念。
当然,从技术重构角度也会发现一些隐含领域知识(概念),这些新的发现也会对领域模型产生影响。
战术模式和通用语言一样,都工作在特定限界上下文内,其应用边界受限界上下文的保护。
战术模式
战术模式的作用是管理复杂性并确保领域模型中行为的清晰明确。可以使用这些模式来捕获和传递领域中的概念、关系、规则。
每个构造块模式都具有单一职责:
- 代表领域中的概念。如实体、值对象、领域服务、领域事件、模块等;
- 用于管理对象的生命周期。如聚合、工厂、仓库等;
- 用于集成或跟踪。领域事件、事件溯源等。
领域建模模式
他们表述实现与模型间的关系,将分析模型绑定到代码实现模型。主要用于在代码中表述模型元素的模式。
实体
实体表述的是领域中的概念,它是由身份而不是属性来定义的。
实体的身份标识在生命周期中保持不变,但其属性会发生变化。实体以身份标识作为唯一凭证,沿着时间轴,记录了实体所有变更事件。
实体的一个实例是产品,一旦产品被生成好,其唯一身份就不会发生变化,但是其描述信息、价格等可以被多次修改。
值对象
值对象代表仅通过数据区分的领域元素和概念。用作模型中元素的描述,它不具有唯一标识。
值对象不需要唯一标识,是因为它总是与另一个对象相关联,是在一个特定上下文中被解析的。通常,其生命周期会依附于它的关联对象(在这里,主要是实体对象)。
值对象会当做不变对象来设计,在完成创建后,其状态就不能改变了。
值对象比较好的例子就是现金,你无需关系货币的身份,只关心它的价值。如果有人用一张五美元钞票交换你的五张一美元钞票,也不会改变五美元本身。
领域服务
在模型中,领域服务封装了不能自然建模为值对象和实体的逻辑、流程和概念。
它本身不具有身份和状态。它的职责是使用实体和值对象编排业务逻辑。
领域服务的一个好例子是运输成本计算器,只要给出一组拖运货物和重量,它就能计算运输成本。
模块
模块主要用于组织和封装相关概念(实体、值对象、领域服务、领域事件等),这样可以简化对较大模型的理解。
应用模块可以在领域模型中促成低耦合和搞内聚的设计。
模块作用于单个领域,用于分解模型规模。子域用于限定领域模型适用范围(有界上下文)。
对象生命周期模式
相对来说,之前提到的模式重点在于表达领域概念。而对象生命周期模式,有点侧重于技术,用于表示领域对象的创建和持久化。
聚合
实体和值对象会相互协作,形成复杂的关联关系。我们需要在满足不变条件的前提下,将其拆分为一个个概念上的整体。
通常,面对复杂的对象关系,在执行领域对象操作时,难以保证一致性和并发性。
领域驱动设计由聚合模式来确保操作的一致性和事务的并发边界。大模型会通过不变性条件来划分,并组成概念化整体的实体和对象组,这个概念化整体便是聚合。
聚合根之间的关系应该通过保持对另一个聚合根 ID 的引用,而非对对象本身的引用来实现。这一原则有助于保持聚合之间的边界并避免加载不必要的对象。
不变性,是在领域模型中强制实现一致性的规则。无论何时对实体或聚合进行变更都要应用该业务规则。
聚合外部的对象只能引用另一个聚合的聚合根,聚合中对象的任何变更都需要通过聚合根来完成。聚合根封装聚合数据并公开行为以对其进行修改。
工厂
如果实体或值对象的创建过程非常复杂,可以将其委托给工厂。工厂会确保在领域对象使用之前就满足所有的不变条件。
如果领域对象很简单并且不具有特殊的不变条件,可以使用构造函数代替工厂。当从持久化存储中重建领域对象时,也可以使用工厂。
仓库
仓库主要用于持久化一个聚合。将聚合作为原子单元进行处理,因此,仓库操作的最小单元就是聚合,每个聚合会对应一个仓库。
仓库是用来检索和存储聚合的机制,是对基础框架的一种抽象。
其他模式
领域事件
领域事件表示问题空间中发生了一些业务人员关心的事情。主要用于表示领域概念。
使用领域事件主要有以下两种场景:
- 记录模型的变更历史;
- 作为跨聚合通信方式。
事件溯源
传统的仅快照式持久化的一个替代项便是事件溯源。作为实体状态存储的替代,可以存储引发该状态的系列事件。
存储所有的事件会提高业务的分析能力,不仅可以得知实体当前状态,还可以得知过去任意时点的状态。
总结
- 实体
- 由唯一标识符定义
- 标识符在整个生命周期保存不变
- 基于标识符进行相等性检查
- 通过方法对属性进行更新
- 值对象
- 描述问题域中的概念和特征
- 不具备身份
- 不变对象
- 领域服务
- 处理无法放置在实体或值对象中的领域逻辑
- 无唯一标识
- 无状态服务
- 模块
- 分解、组织和提高领域模型的可读性
- 命名空间,降低耦合,提供模型高内聚性
- 定义领域对象组间的边界
- 封装比较独立的概念,是比聚合、实体等更高层次的抽象
- 聚合
- 将大对象图分解成小的领域对象群,降低技术实现的复杂性
- 表示领域概念,不仅仅是领域对象集合
- 确定领域一致性边界,确保领域的可靠性
- 控制并发边界
- 工厂
- 将对象的使用和构造分离
- 封装复杂实体和值对象的创建逻辑
- 保障复杂实体和值对象的业务完整性
- 仓库
- 是聚合根在内存中的集合接口
- 提供聚合根的检索和持久化需求
- 将领域层与基础实施层解耦
- 通常不用于报告需求
- 领域事件
- 业务人员所关心的事件,是通用语言的一部分
- 记录聚合根的所有变更
- 处理跨聚合的通信需求
- 事件溯源
- 使用历史事件记录替换快照存储
- 提供对历史状态的查询
本文首发于 GitChat,未经授权不得转载,转载需与 GitChat 联系。
阅读全文: http://gitbook.cn/gitchat/activity/5ca420e5fd80e72ce02ec1ff
您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。