聚合根 (Aggregate Root)
什么是聚合根
| 术语 | 说明 |
|---|---|
| 聚合(Aggregate) | 一组相关联的领域对象(实体 + 值对象)的集合 |
| 聚合根(Aggregate Root) | 聚合中的主实体,聚合中唯一能被外部直接访问的对象,负责维护整个聚合的一致性和完整性 |
聚合根的核心原则
- 边界保护 外部只能通过聚合根操作聚合内的其他对象,不能直接访问聚合内的非根对象,避免数据不一致
- 一致性保证 聚合根负责验证聚合内的所有业务规则,确保聚合内的对象状态始终符合业务约束
- 事务原子性 一个事务只能修改一个聚合根, 从执行上避免分布式事务
- 整体生命周期 聚合由 仓储(Repository) 整体加载和保存
- 唯一领域标识 参考Domain Identity
聚合根设计原则
- 高内聚,低耦合:聚合内的对象必须紧密相关(比如订单和订单项),跨聚合的对象尽量通过聚合根关联,而非直接引用
- 最小化聚合:聚合的范围不宜过大,否则会导致维护复杂、性能下降(比如 “订单” 聚合包含订单、订单项即可,无需包含用户的所有信息)
- 聚合根负责生命周期:聚合根管理聚合内所有对象的创建、修改、删除,外部不能直接操作这些对象
代码实现参考 (结合MyBatis-Plus)
诚然, 主子表的设计非常贴合领域对象实体概念, 主表存储聚合根对象, 子表存储聚合对象关系。
- 所有聚合根必须继承 BaseDO.java
主子表怎么关联
@EntityMapping(thisField = EntityField.RRN, joinField = EntityField.MAIN_REF_KEY) 用来表示主子表关联关系,如 private List<UserRoleDO> userRoleList
java
@Getter
@Setter
public abstract class BaseDO implements Serializable {
/**
* 主键 rrn
*/
@TableId(value = EntityDBField.RRN)
private String rrn;
/**
* 是否删除 默认0代表未删除
*/
@TableLogic
@TableField(value = "is_deleted", fill = FieldFill.INSERT)
private Integer deletedFlag;
/**
* 版本 (乐观锁,保证数据的一致性)
*/
@Version
@TableField(value = "version", fill = FieldFill.INSERT)
private Integer version;
/**
* 创建者
*/
@TableField(value = EntityDBField.CREATOR, fill = FieldFill.INSERT)
private String creator;
/**
* 创建时间
*/
@TableField(value = EntityDBField.CREATE_TIME, fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新者
*/
@TableField(value = EntityDBField.UPDATER, fill = FieldFill.INSERT_UPDATE)
private String updater;
/**
* 更新时间
*/
@TableField(value = EntityDBField.UPDATE_TIME, fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}- 聚合根的子对象(如果有)必须继承 BaseChildDO.java
强烈建议: 子对象不要超过 2 层, 避免嵌套太深带来的性能问题
mainRefKey 字段存什么?
存上级表的唯一标识, 类比领域标识的生成, 结构为 mainRefkey.xx.xxx ... 的 Md5形式;
特殊地, 如果上级表为聚合根, 则存领域标识
java
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
public abstract class BaseChildDO extends BaseDO {
/**
* 关联主表主键
*/
@Code
@TableField(value = EntityDBField.MAIN_REF_KEY)
private String mainRefKey;
}