资源库
资源库(Repository)是对数据访问的一种业务抽象,使其具有业务意义。利用资源库抽象,就可以解耦领域层与外部资源,使领域层变得更为纯粹,能够脱离外部资源而单独存在。注意,仓储接口的定义是在领域层,但是它的实现是在Infrastructure层。引入仓储的好处或者说目标就是让业务逻辑层只关注业务层的逻辑,而不需要关注具体的存储层技术的实现。
之所以引入资源库,主要目的还是为了管理聚合的生命周期。工厂负责聚合实例的生,垃圾回收负责聚合实例的死,资源库就负责聚合记录的查询与状态变更,即记录的“增删改查”操作。不同于活动记录(Active Record)模式,资源库分离了聚合的领域行为和持久化行为。为了更好地管理聚合,领域驱动设计对资源库的设计做了一定程度的限制与规范。
一个聚合对应一个资源库
聚合只有一个入口,那就是聚合根;对聚合生命周期的管理,也只有一个入口,那就是聚合对应的资源库。要访问聚合内的其他实体和值对象,也只能通过聚合对应的资源库进行,这就保护了聚合的封装性。一言以蔽之:通过资源库获取聚合的引用,通过对象图的单一遍历方向获得聚合内部对象。例如,要为订单添加订单项,这样的做法就是错误的:
OrderItemRepository oderItemRepo;
orderItemRepo.add(orderId, orderItem);
OrderItem 不是聚合,不能为其定义资源库。OrderItem 是 Order 聚合的内部实体,因此添加订单项的操作本质上是更新订单的操作:
OrderRepository orderRepo;
Order order = orderRepo.orderOfId(orderId);
order.addItem(orderItem);
orderRepo.update(order);
添加订单项功能由 Order 聚合根实体实现,addItem() 方法的实现可以保证订单领域概念的完整性,实现不变量。例如,该方法可以根据 OrderItem 中的 ProductId 来判断究竟是添加订单项,还是合并订单项,然后修改订单项中所购商品的数量。
资源库的领域特征
1、避免适用和具体的底层存储相关的方法名称:
比如使用save方法,而不insert和update。因为对于业务而言,我关心的就是把模型的数据持久化就好了,业务不关心到底是插入还是更新。
2、方法入参和返回值都要使用领域层的模型对象,不要使用底层的数据层对象。
代码示例
/**
* 用户-Repository接口
*
* @author haoxin
* @date 2021-02-02
**/
public interface UserRepository {
/**
* 通过用户ID获取用户
*
* @param userId
* @return
*/
User find(UserId userId);
/**
* 根据token获取用户
*
* @param token
* @return
*/
User find(Token token);
/**
* 根据手机号获取账号
*
* @param mobile
* @return
*/
List<User> find(Mobile mobile);
/**
* 保存
*
* @param user
*/
UserId store(User user);
/**
* 删除
*
* @param userIds
*/
void remove(List<UserId> userIds);
}