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

news2024/9/21 12:46:46

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

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;
    }

    @Override
    public 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());
    }

    @Override
    public void deleteOrder(String orderId) {
        orderMapper.deleteOrderById(orderId);
        // 同时删除相关的订单历史记录、支付信息和物流信息等
        // orderHistoryMapper.deleteByOrderId(orderId);
        // paymentInfoMapper.deleteByOrderId(orderId);
        // shipmentInfoMapper.deleteByOrderId(orderId);
    }

    @Override
    public 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:更换持久化技术时,需要修改所有涉及数据访问的地方。
仓储模式:只需修改仓储的实现部分,服务层无需变动。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2101985.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机网络 第2章 物理层

文章目录 通信基础基本概念信道的极限容量编码与调制常用的编码方法常用的调制方法 传输介质双绞线同轴电缆光纤以太网对有限传输介质的命名规则无线传输介质物理层接口的特性 物理层设备中继器集线器一些特性 物理层任务&#xff1a;实现相邻节点之间比特&#xff08;0或1&…

后端MVC三层架构,Mybatis ,雪花算法生成唯一id

一.MVC MVC(Model View Controller)&#xff0c;它是一种思想&#xff0c;他把软件系统分为 以下三部分&#xff1a; Model(模型)&#xff1a;用来处理程序中数据逻辑的部分&#xff08;service&#xff0c;dao层&#xff09; View(视图)&#xff1a;在应用程序中&#xff0…

如何把逆地理编码结果表格的不同字段都作为点标注的属性

0.序 很多行业都需要获取一些地点的信息作为gis基础数据。 如消防行业的重点建筑 交通行业的道路 智慧城市的商业楼栋等等。 这些表格信息如何叠加到地图之上&#xff0c;并能够很好的查看各个字段的信息&#xff1f; 本文的重点是把经纬度坐标的Excel表格内容转成kml&…

【Python】数据分析分类图可视化

目录 条形图 箱形图 散点图 分簇散点图 小提琴 分簇小提琴 条形图 条形图是一种直观的图表形式&#xff0c;它通过不同长度的矩形条&#xff08;即“条形”&#xff09;来展示数值变量的中心趋势估计值&#xff0c;其中每个矩形的高度直接对应于该组数据的某个中心量度&…

保存json时,保存成自己喜欢的格式的方法(而不是直接保存成格式化的json文档)

保存json时&#xff0c;不是直接保存成格式化的json文档的格式的方法 前言&#xff0c;博主是如何把格式话的json格式保存成自己喜欢的json格式的保存成格式化的json文档的格式&#xff1a;带缩进格式全部保存成一行每条数据保存成一行&#xff1a; 保存成自己喜欢的格式碎碎念…

《Rust避坑入门记》第1章:挖数据竞争大坑的滥用可变性

赵可菲是一名Java程序员&#xff0c;一直在维护一个有十多年历史的老旧系统。这个系统即将被淘汰&#xff0c;代码质量也很差&#xff0c;每次上线都会出现很多bug&#xff0c;她不得不加班修复。公司给了她3个月的内部转岗期&#xff0c;如果转不出去就会被裁员。她得知公司可…

奇安信天眼--探针/分析平台部署及联动

奇安信天眼–探针/分析平台部署及联动 一 概述二 探针/分析平台部署及联动 1.网络拓扑2.配置流量传感器&#xff08;探针&#xff09; (1)登录控制台(2)配置接口(3)配置默认路由及DNS(4)配置SNMP(5)在探针联动分析平台 3.配置分析平台 (1)登录控制台(2)配置接口(3)配置默认路由…

2024年全国各省路网矢量数据介绍

一、2024年全国路网矢量数据介绍 数据更新时间&#xff1a;2024年5月 数据范围&#xff1a;全国&#xff08;不包含台湾省&#xff09; 数据格式&#xff1a;shp&#xff08;线&#xff09; 数据包含类型&#xff1a;城市主干道、城市次干道、城市快速路、城市支路、高速公…

Python编码系列—Python代码审查的艺术:提升代码质量的黄金法则

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

苹果录屏功能究竟何在?深入探寻苹果设备上的录屏功能:简便、高效、一键达成

在当下这一数字化的时代&#xff0c;不论是教学演示&#xff0c;还是游戏分享&#xff0c;抑或是工作汇报&#xff0c;录屏软件皆已成为我们日常生活中不可或缺之工具。苹果设备以其出类拔萃的用户体验而声名远播&#xff0c;而其内置的录屏功能更是将便捷性与功能性精妙融合。…

TensorFlow介绍二-线性回归案例

一.案例步骤 1.准备数据集&#xff1a;y0.8x0.7 100个样本 2.建立线性模型&#xff0c;初始化w和b变量 3.确定损失函数&#xff08;预测值与真实值之间的误差&#xff09;&#xff0c;均方误差 4.梯度下降优化损失 二.完整功能代码&#xff1a; import os os.environ[TF…

前端脚手架,自动创建远程仓库并推送

包含命令行选择和输入配置&#xff0c;远程仓库拉取模板&#xff0c;根据配置将代码注入模板框架的代码中&#xff0c;自动创建远程仓库&#xff0c;初始化git并提交至远程仓库&#xff0c;方便项目开发&#xff0c;简化流程。 目录结构 创建一个bin文件夹&#xff0c;添加ind…

KAN 学习 Day2 —— utils.py及spline.py 代码解读及测试

在KAN学习Day1——模型框架解析及HelloKAN中&#xff0c;我对KAN模型的基本原理进行了简单说明&#xff0c;并将作者团队给出的入门教程hellokan跑了一遍&#xff0c;今天我们直接开始进行源码解读。 目录 一、kan目录 二、utils.py 2.1 导入库和模块 2.2 逆函数定义 2.3 …

CentOS 7安装Docker详细步骤-无坑-丝滑-顺畅

一&#xff0c;安装软件包 yum install -y yum-utils device-mapper-persistent-data lvm2二&#xff0c;更换yum源为阿里源&#xff1a; yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 三&#xff0c;查看docker版本&…

标准库标头 <optional> (C++17)学习之optional

类模板 std::optional 管理一个可选 &#xfeff;的所含值&#xff0c;即既可以存在也可以不存在的值。 一种常见的 optional 使用情况是作为可能失败的函数的返回值。与如 std::pair<T, bool> 等其他手段相比&#xff0c;optional 可以很好地处理构造开销高昂的对象&a…

【科普】双轴测径仪是根据哪个测量值控制外径尺寸?

单轴测径仪与双轴测径仪都是自带闭环控制功能的在线外径测量设备&#xff0c;单轴测径仪只有一个测头&#xff0c;是根据该测头的检测数据进行控制&#xff0c;这点毋庸置疑&#xff0c;那双轴测径仪这种具备两组测头的设备又是如何控制的&#xff0c;本文就来简单的介绍一下。…

Ubuntu安装网卡驱动

没有无线网 给自己装了双系统后&#xff0c;发现没有无线网络 下载驱动文件 打开终端&#xff0c;输入 lspci -k 能看到&#xff0c;虽然我是RTL8125BG&#xff0c;但use的是r8169: 08:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8125 2.5GbE Controll…

Vue前端路由详解——以Ruoyi框架为案例学习

Vue路由 Vue路由详解_vue 页面路由-CSDN博客 路由模式 Vue 的路由模式&#xff1a;hash 模式和 history 模式的区别_vue路由history和hash的区别-CSDN博客 URL格式&#xff1a; Hash模式&#xff1a;URL中包含#号&#xff0c;用于区分页面部分&#xff0c;实际请求的页面地址…

OpenCV下的无标定校正(stereoRectifyUncalibrated)

OpenCV下的无标定校正(stereoRectifyUncalibrated) 文章目录 1. 杂话2. 无标定校正2.1 先看代码2.2 一点解释2.3 findFundamentalMat参数2.4 stereoRectifyUncalibrated参数 3. 矫正结果 1. 杂话 咱们在之前的帖子里面讲了一些比较常规的标定和校正OpenCV下的单目标定&#xff…

Unity数据持久化 之 文件操作(增删查改)

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正​​ 这里需要弄清几个概念&#xff1a; File&#xff1a;提供文件操作的静态方法&#xff0c;是管理的 Windows.File -…