DDD设计方法-3-仓储,封装持久化数据

2024-09-04 07:52

本文主要是介绍DDD设计方法-3-仓储,封装持久化数据,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前情提要:一共包含 如下六篇文章(篇幅精简,快速入门)

1、初识DDD
2、聚合、实体、值对象
3、仓储,封装持久化数据
4、端口和适配器
5、领域事件
6、领域服务,实现约定

DDD设计方法-3-仓储,封装持久化数据

  • 前言
  • 1、概念 什么是仓储?
  • 2、优缺点 为什么要用?
  • 3、和mapper到底什么区别?有关系吗?
  • 4、实例: 这里吧mapper 和 Repository 都写出来做个比较更直观一些
    • Mapper
      • 定义Mapper接口
      • 定义Mapper接口对应服务层
    • Repository
      • 定义仓储接口
      • 实现仓储接口
      • 使用仓储模式的服务层
  • 这里简单说下两者写法的区别和使用仓储模式的好处

前言

面向对象的设计中,持久化数据的管理和访问是一个关键问题。为了解决这一问题,领域驱动设计(DDD)引入了仓储(Repository)模式。本文将深入探讨仓储模式的概念、作用以及如何实现和使用它来封装持久化数据。

1、概念 什么是仓储?

仓储模式是一种用于管理和访问持久化数据的设计模式。( 可以理解为更高抽象层次的mapper )
它通过提供一个抽象层,将数据存储的细节隐藏起来,使得上层业务逻辑不必直接与数据库交互,从而实现数据访问的解耦合。

2、优缺点 为什么要用?

在这里插入图片描述

先说缺点:
如果你的项目使用crud就非常清晰明了的可以完成你当前的业务 ,那么你引入这层会使你的代码量增加,且基本上属于 事倍功半。
优点:

1、解耦:
将业务逻辑与数据访问分离,提高代码的可维护性和可测试性。
2、易于替换:
由于仓储接口与其实现分离,更换不同的持久化技术时无需修改业务代码。
3、统一访问:
通过仓储模式,可以对数据访问进行统一管理,方便集中控制和优化。

3、和mapper到底什么区别?有关系吗?

相同点 :
1、都是涉及到数据库操作,负责从数据源获取数据,并将数据保存到数据源。
2、解耦:都旨在解耦业务逻辑和数据访问逻辑。

不同点

抽象级别:仓储模式通常在更高的抽象级别上工作,提供的是领域对象的集合操作接口,而Mapper更专注于单个对象与数据库记录之间的映射。

职责范围:仓储模式不仅包括数据映射,还包括业务相关的查询和操作逻辑;而Mapper主要关注对象与数据库之间的映射和简单的CRUD操作。

使用场景:仓储模式更多地在领域驱动设计(DDD)中应用,强调领域对象和持久化的隔离;而Mapper模式更适合直接操作数据库的场景,特别是当使用ORM框架时。

上边说的比较官方 ,我这边白话文在表格中解释一下

关于数据映射操作

mapper 数据映射 做简单的 CRUD 的

仓储模式(Repository Pattern) 前一篇文章我们理解过聚合的概念,可以吧那个思路带入进来,这个是针对多表复杂业务的统一 CRUD的 其中包含业务处理 !

同时由于仓储模式的数据操作时和业务聚合在一起的,
所以会带来另一个优点就是可以隐藏持久化的细节,在更换数据库类型的时候无需大量修改service

4、实例: 这里吧mapper 和 Repository 都写出来做个比较更直观一些

Mapper

定义Mapper接口

public interface OrderMapper {void insertOrder(Order order);void updateOrder(Order order);void deleteOrderById(String orderId);Order selectOrderById(String orderId);// 其他与订单相关的CRUD操作
}

定义Mapper接口对应服务层

服务层A 既做订单的保存,又有删除。

public class OrderServiceWithMapperA {private final OrderMapper orderMapper;public OrderServiceWithMapper(OrderMapper orderMapper) {this.orderMapper = orderMapper;}public void createOrder(Order order) {if (orderMapper.selectOrderById(order.getOrderId()) == null) {orderMapper.insertOrder(order);} else {orderMapper.updateOrder(order);}// 处理订单的历史记录、支付信息和物流信息等// 这里需要调用额外的Mapper对象来处理其他表的CRUD// orderHistoryMapper.insertOrUpdate(order.getHistory());// paymentInfoMapper.insertOrUpdate(order.getPaymentInfo());// shipmentInfoMapper.insertOrUpdate(order.getShipmentInfo());}public void deleteOrder(String orderId) {orderMapper.deleteOrderById(orderId);// 同时删除相关的订单历史记录、支付信息和物流信息等// orderHistoryMapper.deleteByOrderId(orderId);// paymentInfoMapper.deleteByOrderId(orderId);// shipmentInfoMapper.deleteByOrderId(orderId);}public Order getOrder(String orderId) {return orderMapper.selectOrderById(orderId);}
}

服务层B 只做订单的保存。

public class OrderServiceWithMapperB {private final OrderMapper orderMapper;public OrderServiceWithMapper(OrderMapper orderMapper) {this.orderMapper = orderMapper;}public void createOrder(Order order) {if (orderMapper.selectOrderById(order.getOrderId()) == null) {orderMapper.insertOrder(order);} else {orderMapper.updateOrder(order);}// 处理订单的历史记录、支付信息和物流信息等// 这里需要调用额外的Mapper对象来处理其他表的CRUD// orderHistoryMapper.insertOrUpdate(order.getHistory());// paymentInfoMapper.insertOrUpdate(order.getPaymentInfo());// shipmentInfoMapper.insertOrUpdate(order.getShipmentInfo());}public Order getOrder(String orderId) {return orderMapper.selectOrderById(orderId);}
}

Repository

定义仓储接口

import java.util.Optional;public interface OrderRepository {void saveOrder(Order order);void deleteOrder(String orderId);Optional<Order> findOrderById(String orderId);
}

实现仓储接口

public class OrderRepositoryImpl implements OrderRepository {private final OrderMapper orderMapper;public OrderRepositoryImpl(OrderMapper orderMapper) {this.orderMapper = orderMapper;}@Overridepublic void saveOrder(Order order) {if (orderMapper.selectOrderById(order.getOrderId()) == null) {orderMapper.insertOrder(order);} else {orderMapper.updateOrder(order);}// 处理订单的历史记录、支付信息和物流信息等// orderHistoryMapper.save(order.getHistory());// paymentInfoMapper.save(order.getPaymentInfo());// shipmentInfoMapper.save(order.getShipmentInfo());}@Overridepublic void deleteOrder(String orderId) {orderMapper.deleteOrderById(orderId);// 同时删除相关的订单历史记录、支付信息和物流信息等// orderHistoryMapper.deleteByOrderId(orderId);// paymentInfoMapper.deleteByOrderId(orderId);// shipmentInfoMapper.deleteByOrderId(orderId);}@Overridepublic Optional<Order> findOrderById(String orderId) {return Optional.ofNullable(orderMapper.selectOrderById(orderId));}
}

使用仓储模式的服务层

两处仓储的服务层代码基本一致 全为调用 仓储接口

public class OrderService {private final OrderRepository orderRepository;public OrderService(OrderRepository orderRepository) {this.orderRepository = orderRepository;}public void createOrder(Order order) {orderRepository.saveOrder(order);}public void deleteOrder(String orderId) {orderRepository.deleteOrder(orderId);}public Order getOrder(String orderId) {return orderRepository.findOrderById(orderId).orElseThrow(() -> new RuntimeException("Order not found"));}
}

这里简单说下两者写法的区别和使用仓储模式的好处

职责分离:

Mapper:直接与数据库表交互,完成基本的CRUD操作。服务层需要调用多个Mapper以处理复杂的业务逻辑。
仓储模式:仓储模式在Mapper之上添加了一层抽象,将复杂的业务逻辑封装在仓储实现中,服务层只需调用仓储接口即可。

代码简洁性:

Mapper:由于直接操作数据库,服务层可能会变得复杂,需要处理多个Mapper的调用逻辑。
仓储模式:将复杂的业务逻辑封装在仓储层,服务层代码更加简洁和聚焦。

可维护性:

Mapper:当业务逻辑变更时,服务层可能需要修改大量代码。
仓储模式:业务逻辑集中在仓储层,更容易管理和维护。

扩展性:

Mapper:更换持久化技术时,需要修改所有涉及数据访问的地方。
仓储模式:只需修改仓储的实现部分,服务层无需变动。

在这里插入图片描述

这篇关于DDD设计方法-3-仓储,封装持久化数据的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1135431

相关文章

Window Server2016加入AD域的方法步骤

《WindowServer2016加入AD域的方法步骤》:本文主要介绍WindowServer2016加入AD域的方法步骤,包括配置DNS、检测ping通、更改计算机域、输入账号密码、重启服务... 目录一、 准备条件二、配置ServerB加入ServerA的AD域(test.ly)三、查看加入AD域后的变

Window Server2016 AD域的创建的方法步骤

《WindowServer2016AD域的创建的方法步骤》本文主要介绍了WindowServer2016AD域的创建的方法步骤,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、准备条件二、在ServerA服务器中常见AD域管理器:三、创建AD域,域地址为“test.ly”

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

Python MySQL如何通过Binlog获取变更记录恢复数据

《PythonMySQL如何通过Binlog获取变更记录恢复数据》本文介绍了如何使用Python和pymysqlreplication库通过MySQL的二进制日志(Binlog)获取数据库的变更记录... 目录python mysql通过Binlog获取变更记录恢复数据1.安装pymysqlreplicat

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

Java 字符数组转字符串的常用方法

《Java字符数组转字符串的常用方法》文章总结了在Java中将字符数组转换为字符串的几种常用方法,包括使用String构造函数、String.valueOf()方法、StringBuilder以及A... 目录1. 使用String构造函数1.1 基本转换方法1.2 注意事项2. 使用String.valu

Python中使用defaultdict和Counter的方法

《Python中使用defaultdict和Counter的方法》本文深入探讨了Python中的两个强大工具——defaultdict和Counter,并详细介绍了它们的工作原理、应用场景以及在实际编... 目录引言defaultdict的深入应用什么是defaultdictdefaultdict的工作原理

使用Python进行文件读写操作的基本方法

《使用Python进行文件读写操作的基本方法》今天的内容来介绍Python中进行文件读写操作的方法,这在学习Python时是必不可少的技术点,希望可以帮助到正在学习python的小伙伴,以下是Pyth... 目录一、文件读取:二、文件写入:三、文件追加:四、文件读写的二进制模式:五、使用 json 模块读写

Oracle数据库使用 listagg去重删除重复数据的方法汇总

《Oracle数据库使用listagg去重删除重复数据的方法汇总》文章介绍了在Oracle数据库中使用LISTAGG和XMLAGG函数进行字符串聚合并去重的方法,包括去重聚合、使用XML解析和CLO... 目录案例表第一种:使用wm_concat() + distinct去重聚合第二种:使用listagg,

Java后端接口中提取请求头中的Cookie和Token的方法

《Java后端接口中提取请求头中的Cookie和Token的方法》在现代Web开发中,HTTP请求头(Header)是客户端与服务器之间传递信息的重要方式之一,本文将详细介绍如何在Java后端(以Sp... 目录引言1. 背景1.1 什么是 HTTP 请求头?1.2 为什么需要提取请求头?2. 使用 Spr