有关领域驱动设计,主要需要弄清楚以下几点概念:
领域模型
限界上下文
解决方案
领域模型
领域模型是对领域内的概念类或现实世界中对象的可视化表示。又称概念模型、领域对象模型、分析对象模型。它专注于分析问题领域本身,发掘重要的业务领域概念,并建立业务领域概念之间的关系。
这里首先得清楚几个概念:实体、聚合
实体
当一个对象由其 唯一的身份标志 区分、具有可变的特性,这种对象即为实体。
聚合
聚合(Aggregate)是一组相关对象的集合,作为一个整体被外界访问,它由 实体 和 值对象 在 一致性边界之内 组成,聚合根(Aggregate Root)是这个聚合的根节点。
聚合的设计原则
- 在设计聚合时,我们需要慎重的考虑 一致性
- 关注聚合的 一致性边界,在一致性边界之内建模真正的 不变条件(不变条件 指的是业务规则)
- 同一个事务之内不能修改多个 聚合实例
- 在边界之外使用 最终一致性
- 设计 小聚合;(“小” 的极端意思是指 一个聚合只拥有全局标识和单个属性;这种做法不推荐)
- 通过唯一标识来引用其他聚合或实体:当存在对象之间的关联时,建议引用其唯一标识而非引用其整体对象(如果是外部上下文中的实体,引用其唯一标识或将需要的属性构造值对象)
注:如果聚合创建复杂,推荐使用 工厂方法 来屏蔽内部复杂的创建逻辑
领域模型是DDD软件设计方法论中的核心概念,它是业务分析、软件设计的综合结果,是一个系统设计模型。所以要提取出领域模型,首先要先进行业务分析,下面继续用库存来进行讲解。
业务:有一个仓库,里面存储有苹果、香蕉、菠萝等,还有奥利奥、薯片等,还有书籍、杂志、报纸等,每天早上会有一批数量的货进入仓库,每天晚上会将一批数量的货物从仓库提走,苹果、香蕉等的保质期很短,必须在5天内提走,奥利奥、薯片等需要在保质期内提走,书籍、报纸、杂志每年进行一次清理,可能是返厂可能是处理掉,过期没有提走的需要统计后处理掉。
首先我们来总结业务描述中的名词。
仓库
苹果
香蕉
菠萝
奥利奥
薯片
书籍
杂志
报纸
数量
早上货进入仓库
晚上货从仓库提走
保质期
过期被处理
清理
这个名词列表包括了业务的行为主体:角色,以及业务过程中的操作实体:模型,对我们接下来的用例描述、领域模型分析、需求分析很有帮助。当然这个名词列表需要经过进一步分析提炼,成为领域模型。
仓库不是一个实体,是一个聚合
苹果是一个实体
香蕉是一个实体
菠萝是一个实体
奥利奥是一个实体
薯片是一个实体
书籍是一个实体集合
杂志是一个实体集合
报纸是一个实体集合
数量是一个实体属性
早上货进入仓库是一个聚合事件
晚上货从仓库提走是一个聚合方法
保质期是实体的一个属性
过期是实体的一个事件
清理是聚合的一个方法
现在我们来对实体进行抽象,得出以下模型
苹果、香蕉、菠萝是水果
奥利奥、薯片是零食
书籍、杂志、报纸是图书
如果再次进行抽象,就会发现所有以上实体都是商品,最后我们得出的领域模型如下:
以上模型就已经基本对上述业务进行了深度的抽象。如果对应到代码实现,这些实体还需要添加进出数据库相关的方法。
限界上下文
官方给出的解释:限界上下文是业务领域的一个区域,它给予通用语言的任何元素明确无误的含义。
可能解释有点抽象,形象一点的解释就是一个可独立的业务模块,如果以网购为例,物流就是一个独立的业务模块,也就是限界上下文。
如何将业务拆分成不同的限界上下文,这里有条规则:当发现同一个概率再不同领域具有不同定义时,就建议采用不同的限界上下文。这里再以网购为例,同一个商品,在货架上它的关注点是产品介绍及价格,但在库存它的关注点是剩余数量等而不关心价格及产品介绍,所以货架与库存就可以拆分成两个限界上下文。
限界上下文,不仅包含实体,还包含有关实体的业务服务、领域事件等,就按上面的库存为例,库存不仅包含了所有商品的数量(待出库数量、剩余数量等),还会包含与库存有关的业务方法,比如进货,会将指定数量的不同商品添加到剩余数量中,也会包含与库存有关的事件,比如商品被卖出的事件,该事件需要限界上下文减少指定商品的剩余数量并增加该商品的待出库数量。
如上图所示,商品集合是实体类,商品卖出是事件,进货是方法。至于为什么商品卖出是事件而进货时方法呢,原因库存不负责卖商品,它只是被通知商品被卖出。
解决方案
我们在进行DDD领域驱动设计的实践时,会进行需求分析、领域划分、领域建模等工作。而我们的系统要落地,则需要有一套解决方案。
比如我们可以对软件进行分层,如下图所示
再比如我们可以采用仓储来进行实体持久化的管理。
我们可以采用CQRS来实现持久化的读写管理,采用事件溯源来实现对过程的记录。
上面图中包含有很多的概念,先列举一下所涉及的概念:
- Command Bus(命令总线):图中没有,应该放在 Command Handler 之前,可以看作是 Command 发布者。
- Command Handler(命令处理器):处理来自 Command Bus 分发的请求,可以看作是 Command 订阅者、处理者。
- Event Bus(事件总线):一般在 Command Handler 完成之后,可以看作是 Event 发布者。
- Event Handler(事件处理器):处理来自 Event Bus 分发的请求,可以看作是 Event 订阅者、处理者。
- Event Store(事件存储):对应概念 Event Sourcing(事件溯源),可以用于事件回放处理,还原指定对象状态。