阅读参考
- DDD在携程订单实践
- DDD参考阅读
架构
分布式架构,本质上就是化整为零,分而治之的思想。从业务角度去审视我们的系统,领域即问题域,通常是根据一个组织所处的行业进行识别,它基于业务的愿景,定义了系统要解决的现实问题的目标和范围。。
- 核心域:决定产品核心竞争力的子域
- 支撑域:实现核心域目标所需的,但重要程度不如核心域的子域,一般具备强烈的个性化需求
- 通用域:具有通用功能,可被多个子域使用的的是通用域。该子域所解决的问题一般是业界常见问题,有成熟的解决方案,可直接购买或简单修改来使用
界限上下文
限界上下文在《实现领域驱动设计》中,用了很大篇幅去讲,它有几个重要的意义:
- 限界上下文是领域概念的语言边界与业务边界: 在这个边界内,领域概念的内涵是清晰、无歧义的
- 限界上下文是团队的工作边界: 组织边界与限界上下文对齐
- 限界上下文是技术方案的实施边界: 在这个边界内,技术方案是独立自治的,业务逻辑不会落入不同技术边界的间隙
领域设计
领域设计的三个核心问题:
问题一:这个战略核心域的名字是什么,它的目标是什么?
问题二:这个战略核心域中包含哪些概念?
问题三:这个核心域的支撑子域和通用子域是什么?
梳理当前的业务域需要明确业务活动的影响范围,比如某个业务动作或者行为是否会触发下一个业务动作,这个动作(领域事件)的输入和输出是什么,是谁(实体)发出的什么动作(命令),触发了这个动作,这些我们都需要梳理清楚。
下面是一个栗子:
当用户下完订单之后,在仓储领域生成对应的货物拣货单,然后系统根据拣货单中商品给不同分区的仓内作业人员生成对应的拣货任务,作业人员根据对应的拣货任务进行货品的拣取,并放入相应的货物容器中。最终将所有订单商品在拣货完成后进行合流,进行校验,最终形成包裹再进行后续的配送流程。
那这个栗子中拣货单就是一个聚合根,后续的所有紧密业务关联方和事件形成一个界限上下文(聚合),都是围绕这个展开作业。
战略与战术设计说明
- 实体:当一个对象由其标识(而不是属性)区分时,这种对象称为实体。传统的数据建模大多是根据数据库范式设计的,每一个数据库表对应一个实体,实体是拥有唯一标识和状态,且具有生命周期的业务对象。实体通常代表着现实世界中的某个概念,实体与领域模型密切相关,它是领域模型中多个属性、操作或者行为的载体。
实体的代码形态一般有四种形态:- 失血模型:模型仅仅包含数据的定义和getter/setter方法,业务逻辑和应用逻辑都放到服务层中。这种类在Java中叫POJO。
- 贫血模型:贫血模型中包含了一些业务逻辑,但不包含依赖持久层的业务逻辑。这部分依赖于持久层的业务逻辑将会放到服务层中。
- 充血模型:充血模型中包含了所有的业务逻辑,包括依赖于持久层的业务逻辑。
- 胀血模型:胀血模型就是把和业务逻辑不想关的其他应用逻辑(如授权、事务等)都放到领域模型中
- 值对象:值对象主要是属性集合,对实体的状态和特征进行描述。一般只是用来在业务处理时承载数据,若通过对象属性值来识别的对象,则可以认为是一个值对象。如地址信息{“省”: “广东省”,”市”:”深圳市”},我们是通过它的属性来区分出不同的地址。
- 值对象相等性:可以通过对其属性的比较,来区分不同的值对象。
- 不变性:需要保证值对象创建后就不能被修改,即不允许外部再修改其属性。
- 可替换性:值对象是一个整体,当其描述的对象有变化时,需要用一个新的值对象来替换对于值对象,由于其具有不变性,且是通过属性来判断相等的,在设计对应的数据库持久化对象时,可以将其以JSON形式存储在数据库表的某一字段中。
实体可修改,值对象不可修改,只可以整体替换。实体是实实在在的业务对象,值对象只是对对象的描述。值对象依附以实体,实体没了值对象也就没了。
- 聚合和聚合根:一个业务流程中,一般会同时涉及多个实体、值对象的操作,这里业务逻辑紧密的实体和值对象便组合成一个聚合。
- 在一致性边界之内确保不变性:聚合用来封装真正的不变性,而不是简单地将对象组合在一起。聚合内有一套不变的业务规则,各实体和值对象按照统一的业务规则运行,实现对象数据的一致性。
- 设计小聚合:如果聚合聚合包含过多的实体,会提高管理实体的复杂性,高频操作下容易并发冲突,降低了系统的性能
- 在边界之外使用最终一致性:不同的聚合之间不要求强一致性,保证最终一致性。一次事务操作中,只修改一个聚合实例,如果需要修改多个实例,可以考虑通过异步的方式保证最终一致性。
聚合根是实体,拥有实体的业务属性和行为,同时也是聚合的管理者,负责协调聚合内的实体和值对象,按照固定的业务规则,完成业务逻辑。
聚合根是主要的业务逻辑载体,DDD中所有的战术实现都围绕着聚合根展开。
一个大而全的聚合根比如:
1
2
3
4
5
6
7
8
9
10
public class Blog{
//聚合根Blog的全局唯一标识
private BlogId blogId;
private String title;
//通过唯一标识引用其他聚合
private AuthorId authorId;
private PostId postId;
private Set<comment> comments;
private Set<tag> tags;
}
- 领域服务:领域中的服务表示一个无状态的操作,它用于实现特定于某个领域的任务。
- 当某个操作不适合放在聚合(实体)或值对像上时,最好的方式便是使用领域服务。
- 领域事件:表示领域中发生的事件。一个领域事件将导致进一步的业务操作,在实现业务解耦的同时,还有助于形成完整的业务闭环。
- 领域事件含义很广泛,可以是业务流程的一个步骤,也可以是一个事件发生后触发的后续动作,缴费完成之后,触发短信通知;
DDD的分层结构
一个主要的变化是将业务逻辑层的服务拆分到了应用层和领域层。应用层响应业务用例的变化。