领域设计战术:解密仓储模式(Repository)与数据访问对象(DAO)的设计区别
在开展面向对象系统设计时,许多开发人员常常将**仓储模式(Repository)**与传统的**数据访问对象(DAO, Data Access Object)**相混淆,认为它们只是对 SQL 调用进行了相似的包装。这种认知上的错觉,往往导致微服务核心领域逻辑外泄,从而使系统重新退化为贫血模型。
事实上,Repository 和 DAO 在系统架构中的定位、面向的主体、以及对数据一致性的控制边界上有着根本性、原则性的差异。
本文将系统拆解 Repository 与 DAO 的架构维度对比、防腐转换拓扑、以及在 Spring Data JPA/MyBatis 混合架构下的工程代码落地。
一、 战术定位对比:面向集合 vs. 面向数据库
我们通过下表拆解两种持久化战术组件在 DDD 架构中的深层次差异:
| 特征维度 | 数据访问对象 (DAO) | 仓储模式 (Repository) |
|---|---|---|
| 面向的主体 | 面向**数据库物理表**。一个 DAO 通常对应一张物理表。 | 面向**业务聚合根 (Aggregate Root)**。一个聚合根仅对应一个 Repository。 |
| 架构所处层次 | 基础设施层(Infrastructure)。直接与底层 SQL 驱动挂钩。 | **接口声明在领域层 (Domain),具体实现注入在基础设施层**(防腐倒置)。 |
| 返回的数据实体 | 数据库 DTO / PO 对象(贫血的数据实体)。 | **纯净的充血领域聚合实体 (Entity / Aggregate Root)**。 |
| 业务语义表达 | 面向 CRUD 物理操作。如 insert(), update()。 | 面向**内存集合模拟**。如 save(), find()。 |
二、 仓储的防腐数据流转拓扑
Repository 在架构中充当了**阻断物理数据结构污染核心领域层的防腐屏障**。其数据流转拓扑如下:
[ 应用服务层: Application Service ]
│
▼ 1. 调用 Repository.save(OrderAggregate)
[ 领域层接口: OrderRepository (Interface) ]
│ (依赖倒置: 实现类在基础设施层)
▼
┌────────────────────┼────────────────────┐
│ [ 基础设施层: RepositoryImpl 实现 ] │
│ │
│ 1. 接收纯净充血聚合: OrderAggregate │
│ 2. 翻译器清洗转换 (OrderTranslator) │
│ 将 OrderAggregate 拆解转换为: │
│ - OrderPO (订单主表PO) │
│ - OrderItemPO (订单明细表PO) │
│ 3. 调用底层的 JPA/MyBatis DAO 执行落盘 │
│ - OrderDAO.insert(orderPo) │
│ - ItemDAO.batchInsert(itemPos) │
└─────────────────────────────────────────┘
---三、 代码实战:订单聚合根的仓储实现
以下展示了如何在基础设施层实现领域层声明的 OrderRepository 接口,并完成聚合根到物理 PO 的防腐转换:
1. 领域层:声明仓储接口(Domain Layer)
package com.company.sales.domain.repository;
import com.company.sales.domain.model.SalesOrder;
/**
* 订单仓储接口:仅面向领域实体,拒绝任何 DB/SQL 依赖
*/
public interface OrderRepository {
SalesOrder findById(String orderId);
void save(SalesOrder order);
}
2. 基础设施层:仓储物理实现与 DAO 装配(Infrastructure Layer)
package com.company.sales.infra.repository;
import com.company.sales.domain.model.SalesOrder;
import com.company.sales.domain.repository.OrderRepository;
import com.company.sales.infra.dao.*;
import com.company.sales.infra.po.*;
import org.springframework.stereotype.Repository;
import java.util.*;
@Repository
public class OrderRepositoryImpl implements OrderRepository {
private final OrderParentDAO orderParentDao; // 底层物理单表 DAO
private final OrderItemDAO orderItemDao;
public OrderRepositoryImpl(OrderParentDAO parentDao, OrderItemDAO itemDao) {
this.orderParentDao = parentDao;
this.orderItemDao = itemDao;
}
@Override
public SalesOrder findById(String orderId) {
// 1. 调用底层物理 DAO 分别拉取主表与子表 PO
OrderParentPO parentPo = orderParentDao.selectById(orderId);
List<OrderItemPO> itemPos = orderItemDao.selectByOrderId(orderId);
// 2. 通过翻译器,将多张物理表 PO 组装重建为单个完整的充血订单聚合根返回
return OrderTranslator.toDomain(parentPo, itemPos);
}
@Override
public void save(SalesOrder order) {
// 1. 将充血聚合拆解为扁平的物理表 PO
OrderParentPO parentPo = OrderTranslator.toParentPo(order);
List<OrderItemPO> itemPos = OrderTranslator.toItemPos(order);
// 2. 依次调用底层 DAO 执行物理更新,保护事务一致性
orderParentDao.saveOrUpdate(parentPo);
orderItemDao.batchSaveOrUpdate(itemPos);
}
}
---四、 总结
将 Repository 与 DAO 彻底分离,是构筑弹性、清洁微服务架构的必经之路。
它通过**基于接口的依赖倒置设计,确保了数据库存储介质(SQL/NoSQL)的更迭不会向核心领域代码渗透,保障了领域逻辑的高度纯净与可测试性**;并**通过面向聚合根的集合装配模式,强行守护了聚合根的事务与业务一致性边界**。掌握这套仓储防腐与底层数据对象转换技术,是主导大中型企业架构升级、防范系统退化为泥潭代码的看家基本功!
本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。
评论交流 (0)
您尚未登录,请先 登录 后发表评论!



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