DDD 对于团队的上手成本太高了,需要每个人都理解 DDD 的思想,还要有个大佬开路,做决策,否则玩不起来,至少目前我司是弄不起来了,但是学习下其中的一些思想,对平常开发编写代码是大有裨益。
—— 2022-02-21
DDD 核心
- 辩证的看待每一个术语,灵活变动
- 事件驱动
- 既要耦合,又要解耦
- DDD 只是一套方法论,目的是写出好维护,易扩展的代码,借鉴它的思想和一些方法,并不是一昧的提倡用DDD,还是要根据业务和团队水平进行实施
DDD 的难点
建立领域模型
DDD 后续的操作都基于领域模型,所以一开始拿到需求后,应该是展开事件风暴,分析建立领域模型。
例如用户基本业务属性有:用户名,真实姓名,手机号,密码,所属省,所属市,所属区,所属单位,关联角色。传统的 CRUD 做法我们会创建一个用户表来维护这些属性,但是在 DDD 中,我们会对这里分析出事件,分别是:用户被新增事件、用户名被修改事件、用户被删除事件。从事件可以推出,用户是我们的领域模型,那这个用户领域模型,应该包含哪些字段呢?
用户存在这些属性:id,用户名,真实姓名,手机号,密码,所属省,所属市,所属区,所属单位,关联角色。
对于用户领域模型来说,省、市、区地址相关属性是值对象,对用户领域模型来说不存在独立的生命周期。而角色是有独立的生命周期,能独立处理业务逻辑,是一个独立的限界上下文。
通过业务逻辑,来定义领域模型,将领域能力定义在领域模型内部。
定义用户聚合根:
- AggregateRoot 聚合根标识
- 聚合根中的属性不是和数据库字段一一对应的,而是更偏向于业务语义
聚合根中没有 set 方法,因为每一个属性的变更是一个业务逻辑,传统的做法是放在业务上去处理,DDD 中属性的变更应该是在模型内的,模型是具有感知能力的
/*** 用户聚合根** @author baiyan*/@Getter@Builder@NoArgsConstructor@AllArgsConstructor@Slf4jpublic class User implements AggregateRoot {/*** 用户id*/private Long id;/*** 用户名*/private String userName;/*** 用户真实名称*/private String realName;/*** 用户手机号*/private String phone;/*** 用户密码*/private String password;/*** 用户地址*/private Address address;/*** 用户单位实体*/private Unit unit;/*** 角色实体*/private List<Role> roles;/*** 创建时间*/private LocalDateTime gmtCreate;/*** 修改时间*/private LocalDateTime gmtModified;//省略部分逻辑/*** 修改用户名** @param userName 修改后的用户名* @param existUser 根据修改后的用户名查询出来用户*/public void bindUserName(String userName,User existUser){ValidationUtil.isTrue(Objects.isNull(existUser) || Objects.equals(existUser.getId(),this.id),"user.user.name.is.exist");this.userName = userName;}}
定义地址值对象:ValueObject 是值对象标识 ```java /**
- 地址值对象 *
@author baiyan */ @Getter @AllArgsConstructor @NoArgsConstructor public class Address implements ValueObject
{/**
- 省 */ private String province;
/**
- 市 */ private String city;
/**
- 区 */ private String county;
/**
- 比较地址相等 *
- @param address 地址
- @return */ @Override public boolean sameValueAs(Address address){ return Objects.equals(this,address); }
}
<a name="Fcp1N"></a>## 仓储层仓储层是将领域模型映射成数据模型,只是简单的模型映射转换,但是要注意的是,DDD 中对同一数据不允许有多个修改入口,这样会导致数据一致性难以维护或者说业务逻辑重复。所以针对这里角色和用户是多对多的关系,传统的做法会设计一个表来维护角色和用户的关系,但是这样会有两个数据模型,和前面提到的有冲突了,因此不能单独设计一个表来维护,而是把这些关系放在用户表中来维护。用户表:```java@Data@EqualsAndHashCode(callSuper = true)@TableName("t_user")public class UserPO extends BaseUuidEntity {/*** 用户名* */private String userName;/*** 真实姓名* */private String realName;/*** 手机号* */private String phone;/*** 密码* */private String password;/*** 关联的单位id** */private Long unitId;/*** 关联的角色id列表* */private String roleIds;/*** 省*/private String province;/*** 市*/private String city;/*** 区*/private String county;}
DDD 术语
领域
领域是一个业务方向,例如电商中,订单、物流、商品这些都是划分为一个领域。
子域
子域是对领域的二次划分。例如领域订单的子域可以划分为子订单、母订单。
核心域
支撑域
通用域
它的核心体现在高兼容性,在订单中可以用,挪到物流中也可以用。
限界上下文
用来定义领域业务的边界
防腐层
上下文A和上下文B不能直接调用而是要通过防腐层来处理,非常重要
