四、什么是仓储层?
在DDD中,仓储(Repository)是一种设计模式,它充当了领域层与数据存储层之间的桥梁。仓储的主要职责是提供一种抽象机制,使得领域对象(尤其是聚合根)可以被透明地持久化和检索,而无需暴露底层的数据访问技术细节给领域层。这样设计的目的是为了保持领域模型的纯净性,让业务逻辑不受数据访问技术的影响,同时也促进了代码的可测试性和可维护性。
主要特点
接口定义在领域层
- 仓储接口定义了诸如保存(save)、查找(find)、更新(update)和删除(delete)领域对象的方法,这些接口属于领域层从一部分,确保了领域逻辑的独立性
实现位于基础设施层
- 虽然仓储接口属于领域层,但它的具体实现通常位于基础设施层(Infrastructure Layer)。实现细节回依赖于具体的持久化技术,如关系数据库、NoSQL数据库、内存数据库等
隔离领域层和数据访问层
- 通过仓储模式,领域层的对象可以不知道也不关心数据是如何存储和检索的。这有助于降低各层之间的耦合度,使得领域逻辑更加集中和纯粹
提供一致的领域对象视图
- 仓储还负责管理领域对象的生命期,确保从存储中检索出来的对象符合领域模型的规则,例如,确保聚合的一致性
支持领域驱动设计的原则
- 如
Ubiquitous Language(通用语言)
、业务优先和关注点分离等原则,通过仓储的抽象,可以更好地在代码中体现业务领域的概念和规则
问题探讨
仓储层和聚合根的区别是什么?
浅显解释
聚合根
- 想象你有一个装满东西的盒子,这个盒子就是聚合根。盒子里的东西(比如小玩具、文具等)代表聚合内的其他实体和值对象。盒子外面有个标签,写着里面有什么,这就是聚合根的标识。当你相拿走或放回盒子里的东西是,必须通过盒子(聚合根)来做,不能直接对盒子里的物品动手脚。聚合根确保盒子里的东西按规则摆放,保持整齐(即业务规则的一致性)
仓储层
- 想象有一个大仓库,里面存放着很多这样的盒子(聚合根)。仓库管理员(仓储层)知道怎么找到每一个盒子,并且能帮你把盒子放进仓库货从仓库里拿出来。你告诉管理员需要哪个盒子,管理员就会处理好一切,你不需要知道仓库是怎么排列这些盒子的,也不用管它是用叉车还是传送带搬运的(即数据访问的具体实现)
因此,聚合根是关于管理业务对象内部的规则和结构,而仓储层是关于如何保存和获取这些业务对象到数据存储中。聚合根关注"做什么",仓储层关注"怎么存取"。
术语解释
聚合根
- 概念:聚合根是聚合中的一个特殊实体,它作为聚合的入口点,负责维护聚合内部的一致性。聚合是一组相关对象的集合,这些对象一起被当作一个单元来对待,对外界隐藏起内部结构和复杂性
- 职责:聚合根确保所有的业务规则在聚合内部得到遵守,它控制着对聚合内其他实体和值对象的访问。外部对象不能直接访问聚合内的非根实体,只能通过聚合根进行操作
- 示例:在一个电子商务系统中,订单
Order
可能是一个聚合根,它管理者订单项OrderItem
、客户信息Customer
等内部实体,确保例如订单总额的计算规则得到正确执行
仓储层
- 概念:仓储是一个设计模式,它提供了一种抽象机制,用于透明地持久化和检索领域对象,尤其是聚合根。它就像是一个集合或数据库的模拟,允许领域层以面向对象的方式操作数据,而无需关心数据存储的具体细节
- 职责:仓储的主要职责包括保存
Save
、查询Find
、更新Update
和删除Delete
聚合根。它隔离了领域层和数据访问层,使得领域逻辑可以独立于具体的数据库技术 - 示例:在一个电子商务系统中,
OrderRepository
可能提供方法来查找特定ID的订单、保存新订单到数据库、更新订单状态等,而不需要订单类知道这些操作是如何与数据库交互的
区别总结
- 目的不同:聚合根关注于业务逻辑的封装和领域内的不变性维护,而仓储关注于领域对象的持久化和检索机制
- 层次位置:聚合根是领域层的一部分,直接参与业务逻辑的实现;仓储虽然定义在领域层,但起具体实现通常位于基础设施层,作为领域层与数据存储之间的适配层
- 交互方式:外部组件通过仓储来操作聚合根,而不会直接操作聚合内部的其他实体。聚合根则负责维护其内部状态和业务规则的一致性
简而言之,聚合根是领域模型中的核心构造,负责业务逻辑的实现和一致性保护;而仓储是领域层与数据存储之间的中介,确保领域对象能够被持久化和检索,同时保持领域模型的纯粹性
代码示例
写一个订单仓库 (OrderRepository)
public interface OrderRepository {
/**
* 保存订单聚合根。
* @param order 待保存的订单聚合根对象,包含订单的所有相关信息。
* @return 保存后的订单聚合根对象。
*/
OrderAggregate save(OrderAggregate order);
/**
* 根据订单ID查找订单聚合根。
* @param orderId 待查找订单的唯一标识。
* @return 如果找到,则返回包含订单信息的Optional对象;否则返回空Optional。
*/
Optional<OrderAggregate> findById(UUID orderId);
/**
* 删除订单聚合根。
* @param order 待删除的订单聚合根对象。
*/
void delete(OrderAggregate order);
/**
* 更新订单状态。
* @param orderId 待更新订单的状态的唯一标识。
* @param newStatus 新的订单状态信息。
*/
void updateOrderStatus(UUID orderId, OrderStatusVO newStatus);
}
写一个基于内存的订单仓库实现类OrderRepositoryImpl
public class OrderRepositoryImpl implements OrderRepository {
/**
* 使用HashMap存储订单聚合体,以订单ID作为键,订单聚合体作为值。
*/
private Map<UUID, OrderAggregate> orders = new HashMap<>();
/**
* 保存订单聚合体。
*
* @param order 要保存的订单聚合体。
* @return 返回保存的订单聚合体。
*/
@Override
public OrderAggregate save(OrderAggregate order) {
orders.put(order.getOrderId(), order);
return order;
}
/**
* 根据订单ID查找订单聚合体。
*
* @param orderId 要查找的订单ID。
* @return 返回找到的订单聚合体的Optional,如果找不到则为empty。
*/
@Override
public Optional<OrderAggregate> findById(UUID orderId) {
return Optional.ofNullable(orders.get(orderId));
}
/**
* 删除订单聚合体。
*
* @param order 要删除的订单聚合体。
*/
@Override
public void delete(OrderAggregate order) {
orders.remove(order.getOrderId());
}
/**
* 更新订单状态。
*
* @param orderId 要更新状态的订单ID。
* @param newStatus 新的订单状态。
*/
@Override
public void updateOrderStatus(UUID orderId, OrderStatusVO newStatus) {
Optional<OrderAggregate> optionalOrder = findById(orderId);
optionalOrder.ifPresent(order -> {
order.changeStatus(newStatus);
});
}
}
OrderRepositoryImpl
在DDD中所处位置?
属于基础设施层(Infrastructure Layer)的一部分。基础设施层提供技术性服务,如数据库访问、消息传递等,以支持领域层和应用层的功能。
具体分析,OrderRepository
是一个接口,它定义了如何存取领域对象OrderAggregate
到持久化存储(如数据库)中。这个接口属于领域层或核心域(Core Domain),因为它表达了领域逻辑的一部分需求,即如何管理订单聚合根的存储和检索。
而OrderRepositoryImpl
作为接口OrderRepository
的一个实现,通常驻留在基础设施层。它实现了与具体数据存储技术(如JDBC、Hibernate、Spring Data JPA等)交互的细节,确保领域层可以透过一个抽象的接口与这些底层技术解耦。这样,领域层的代码就不需要关心数据是如何被存储或检索的,从而提高了代码的可维护性和灵活性。