广告
您当前的位置: 首页 >  技术 >  编程开发

DDD 实践:解密 DDD 仓储模式(Repository)与传统 DAO 的物理分层边界

作者:CoderWang 时间:2026-07-03 阅读数:5人阅读

在传统的持久层开发中,我们习惯于创建 UserDAOUserMapper,并在其中直接编写面向数据库表的 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)分层解耦的灵魂。

它通过**在领域层定义接口、在基础设施层实现依赖倒置**,成功隔离了底层持久化数据库技术;并通过**只对聚合根提供服务接口**,捍卫了微服务的事务边界。掌握这一模式的落地细节,是编写易扩展、抗技术迭代微服务框架的核心基石!

本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。

评论交流 (0)

正在加载评论...
头像

CoderWang

当你还撑不起你的梦想时,就要去奋斗。如果缘分安排我们相遇,请不要让她擦肩和过。我们一起奋斗!

微信