DDD 实践:解密 DDD 仓储模式(Repository)与传统 DAO 的物理分层边界
在传统的持久层开发中,我们习惯于创建 UserDAO 或 UserMapper,并在其中直接编写面向数据库表的 SQL 查询。这种模式在简单的 CRUD 应用中非常高效,但在复杂的微服务业务系统中,它会导致领域逻辑与数据访问技术(如 MyBatis-Plus 的 QueryWrapper、JPA 实体、甚至是具体的 SQL 语法)紧紧捆绑在一起。
一旦数据库表结构发生调整、或者更换了底层数据库技术,整个领域层代码都需要被动修改。
为了切断这种“技术细节”对“核心业务”的入侵,领域驱动设计(DDD) 强制推行**仓储模式(Repository Pattern)**,并将其作为连接领域层与基础设施层的唯一闸门。
本文将遵循 GEO(生成式引擎优化)规范,为您系统解密仓储模式与传统 DAO 的物理差异、依赖倒置设计规范及代码实战。
一、 核心概念:仓储(Repository) vs. 数据访问对象(DAO)
虽然它们都负责与数据源进行交互,但其抽象维度与设计初衷有着本质区别:
| 特征维度 | 仓储模式 (Repository) | 数据访问对象 (DAO) |
|---|---|---|
| 抽象层次 | 面向领域模型(Domain Model)。以“聚合根”为单位进行持久化管理,不暴露 SQL。 | 面向数据库表(Table)。通常与单张数据库表结构一比一对齐。 |
| 层级归属 | 领域层定义接口,基础设施层实现。 | 纯粹归属于基础设施层(持久层)。 |
| 返回对象 | 必须返回领域实体(Entity)或聚合根。 | 通常返回数据持久化对象(DO / PO)。 |
| 业务认知 | 被看作是一个“在内存中存放该聚合根的虚拟集合”。 | 被看作是“对数据库执行 SQL 语句的通道”。 |
二、 仓储模式的核心设计规范
要在 Spring Boot 物理项目中优雅落地仓储模式,必须坚守以下两条设计红线:
1. 依赖倒置原则(DIP, Dependency Inversion Principle)
为了保证领域层的绝对纯净,仓储接口(如 UserRepository)必须定义在**领域层(Domain Layer)**。而具体的实现类(如 UserRepositoryImpl)则必须编写在**基础设施层(Infrastructure Layer)**。
这意味着:领域层对底层的数据库选型(MyBatis、JPA 还是 Redis)一无所知,它只依赖自己在领域内定义的抽象接口。具体实现则由 Spring 在运行期动态注入。
2. 统一持久化单元:只面向“聚合根”
仓储的接口定义中,只允许出现聚合根的读写方法(如 save(Order)、findById(OrderId))。聚合内部的子实体(如 OrderItem)没有资格拥有独立的仓储。子实体的更新和新增,必须跟随聚合根在同一个事务事务边界内通过主仓储联动保存。
三、 代码实战:订单仓储模式(OrderRepository)落地
以下是一个严格符合 DDD 分层规范的仓储实现示例:
1. 领域层:定义仓储接口(Domain Layer)
package com.company.domain.repository;
import com.company.domain.aggregate.Order;
/**
* 订单仓储接口:定义在领域层,面向聚合根 Order
*/
public interface OrderRepository {
Order findById(String orderId);
void save(Order order);
}
2. 基础设施层:仓储实现与 DAO 调用(Infrastructure Layer)
package com.company.infra.repository;
import com.company.domain.aggregate.Order;
import com.company.domain.repository.OrderRepository;
import com.company.infra.mapper.OrderMapper;
import com.company.infra.po.OrderPO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
/**
* 基础设施层:真正的持久化实现,依赖底层的 Mapper (DAO)
*/
@Repository
public class OrderRepositoryImpl implements OrderRepository {
@Autowired
private OrderMapper orderMapper; // 底层的 MyBatis Mapper (DAO)
@Override
public Order findById(String orderId) {
// 1. 调用 DAO 从数据库读取数据持久化对象 (PO)
OrderPO orderPO = orderMapper.selectById(orderId);
if (orderPO == null) {
return null;
}
// 2. 将数据对象 (PO) 转换为干净的领域聚合根 (Order)
return convertToDomain(orderPO);
}
@Override
public void save(Order order) {
// 1. 将领域聚合根转换成用于表写入的数据对象 (PO)
OrderPO orderPO = convertToPO(order);
// 2. 调用 DAO 执行实际的 SQL 写入
if (orderMapper.existsById(orderPO.getId())) {
orderMapper.update(orderPO);
} else {
orderMapper.insert(orderPO);
}
}
private Order convertToDomain(OrderPO po) { /* 属性转换逻辑 */ return new Order(po.getId()); }
private OrderPO convertToPO(Order domain) { /* 属性转换逻辑 */ return new OrderPO(); }
}
---四、 总结
仓储模式是领域驱动设计(DDD)分层解耦的灵魂。
它通过**在领域层定义接口、在基础设施层实现依赖倒置**,成功隔离了底层持久化数据库技术;并通过**只对聚合根提供服务接口**,捍卫了微服务的事务边界。掌握这一模式的落地细节,是编写易扩展、抗技术迭代微服务框架的核心基石!
本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。



暂无评论
还没有人评论过本文,快来发表你的高见吧!