DDD学习

概述

学习一下DDD

什么是DDD

DDD是领域驱动设计(Domain Driven Design)的缩写,是一种软件开发方法论。它强调将业务领域划分为多个紧凑的、自包含的领域,并通过强调领域模型的重要性来建立一个通用的语言和理解。

DDD鼓励开发者们更多地关注业务本身,而不是纯粹的技术实现。在DDD中,开发者将应用程序分为多个相互依赖但相对独立的领域,并通过领域模型来描述这些领域的内部结构和行为。通过使用聚合、实体、值对象、服务等概念,开发者可以更好地表示领域的真实世界,并将其映射到软件系统中。

DDD还提供了许多有用的设计模式,例如事件溯源、领域事件、限界上下文等,以帮助开发者更好地组织代码、管理复杂性,并提高系统的可维护性和可扩展性。同时,DDD还强调团队协作和沟通的重要性,通过建立通用的语言和理解来消除偏差和误解,从而推动项目的成功。

总之,DDD是一种注重业务本质、强调领域建模、提高设计品质和代码质量的软件开发方法论,其目标是构建高质量、易维护、适应变化的软件系统。

DDD的springboot工程目录结构

在使用DDD的情况下,一个Spring Boot项目的目录结构应该如下:

├── src
│   ├── main
│   │   ├── java
│   │   │   ├── com.example.myapp
│   │   │   │   ├── config
│   │   │   │   │   └── AppConfig.java
│   │   │   │   ├── domain
│   │   │   │   │   ├── model
│   │   │   │   │   │   ├── AggregateRoot.java
│   │   │   │   │   │   ├── Entity.java
│   │   │   │   │   │   ├── ValueObject.java
│   │   │   │   │   │   └── ...
│   │   │   │   │   ├── repository
│   │   │   │   │   │   ├── UserRepository.java
│   │   │   │   │   │   └── ...
│   │   │   │   │   ├── service
│   │   │   │   │   │   ├── UserService.java
│   │   │   │   │   │   └── ...
│   │   │   │   │   └── ...
│   │   │   │   ├── infrastructure
│   │   │   │   │   ├── persistence
│   │   │   │   │   │   ├── UserRepositoryImpl.java
│   │   │   │   │   │   └── ...
│   │   │   │   │   └── messaging
│   │   │   │   │       ├── MessagePublisher.java
│   │   │   │   │       └── ...
│   │   │   │   └── MyApplication.java
│   │   │   └── ...
│   │   ├── resources
│   │   │   ├── application.yml
│   │   │   └── ...
│   │   └── ...
│   └── test
│       ├── java
│       │   └── ...
│       └── resources
│           └── ...
└── ...

在这个结构中,主要包含了以下几个部分:

  • config:用于存放应用程序的配置类;
  • domain:用于存放领域层相关的代码,包括实体、聚合根、值对象等;
  • infrastructure:用于存放基础设施层相关的代码,包括数据访问、消息发送等;
  • MyApplication:应用程序的入口文件。

此外,在domaininfrastructure目录下还会有对应的单元测试和集成测试代码。

另外值得注意的是,在DDD中,我们通常会将代码组织成基于业务领域(bounded context)的模块,因此上述目录结构并不是固定的,具体的组织方式应该根据应用程序的需求进行调整。

DDD与常用的Controller、Service、Dao分层有什么不同

常用的 Controller、Service、Dao 分层是一种基于三层架构的分层模式,主要关注技术实现方面的分层,以提高代码的可读性、可维护性和可扩展性等目标。在这种分层模式下,通常会将应用程序按照控制层、服务层、数据访问层进行划分,每个层次都有特殊的职责和工作。

而 DDD 则是一种面向领域的设计方法,它强调将业务逻辑与底层技术实现分离,并通过领域模型来描述和理解业务和系统的本质。DDD 认为应用程序开发的核心是领域模型的设计和实现,因此需要将软件设计和实现过程中的精力放在对领域的深入了解上,同时尽可能地消除技术实现带来的干扰。

DDD 与常用的 Controller、Service、Dao 分层的不同之处在于 DDD 更加注重领域的建模和设计,而常用的分层则更加注重技术实现的分层。在 DDD 中,核心思想是将具体的业务逻辑和领域知识进行建模,通过分析业务场景和业务需求,抽象出领域模型和相应的业务规则。这样可以将业务逻辑嵌入到领域模型中,从而更加清晰地表达业务需求和业务流程,同时也使得系统更加容易扩展和维护。

Controller,Service,Dao分层相比于DDD有什么优缺点

常用的 Controller、Service、Dao 分层是一种基于三层架构思想的分层设计模式,具体如下:

  1. Controller 层:主要负责处理用户请求,并将请求转发给 Service 层进行处理。通常包含 URL 映射、参数校验、控制事务、数据格式转换等功能。

  2. Service 层:主要负责业务逻辑的处理和实现,接收来自 Controller 层的请求,并根据业务需求调用 Dao 层提供的数据访问接口进行 CRUD 操作。同时,Service 层也负责对业务逻辑进行封装,提供接口给外部系统或内部其他系统使用。

  3. Dao 层:主要负责数据访问,通过与数据库连接,实现对数据的增删改查等操作,并提供与业务逻辑相对应的接口给 Service 层使用。

相比于 DDD 的目录结构,常用的 Controller、Service、Dao 分层更侧重于技术实现方面,而不关注业务场景中的领域划分和领域专家参与。其优缺点如下:

优点:

  1. 设计简单易懂:常用的 Controller、Service、Dao 分层虽然简单,但能够清晰地把职责分离,代码易于理解和维护。

  2. 灵活性强:常用的 Controller、Service、Dao 分层基于技术实现,因此在开发过程中可以灵活应对变化,快速迭代产品需求。

  3. 适用范围广:常用的 Controller、Service、Dao 分层可以适用于大部分业务场景,通用性较强。

缺点:

  1. 难以应对复杂业务场景:常用的 Controller、Service、Dao 分层没有强调业务逻辑建模,无法支撑大规模高复杂度业务的开发。

  2. 分层划分不够精细:常用的 Controller、Service、Dao 分层划分较为简单,无法有效地解决复杂业务场景下的问题。例如,在传统分层结构下,往往会存在因过多重复或耦合导致的代码臃肿和可维护性差等问题。

  3. 开发过程中需要考虑数据和业务之间的映射关系:常用的 Controller、Service、Dao 分层虽然创建了三个分层,但开发过程中仍需要考虑数据和业务之间的映射关系,增加了开发难度。

综上所述,虽然常用的 Controller、Service、Dao 分层与 DDD 的目录结构差异较大,但在不同场景下都有各自适用的情况。

具体场景对比

假设我们要开发一个电商平台,需要实现一个下单功能。我们来比较一下常用的分层与 DDD 的实现方式。

常用的分层实现

在常用的分层实现中,我们可能会按照控制层、服务层、数据访问层进行划分,大概的流程如下:

  1. 控制器接收到用户的请求,验证参数合法性。
  2. 服务层负责根据业务逻辑进行相应的处理,例如查询商品信息、检测库存情况等等。
  3. DAO层通过数据库操作,对库存情况进行更新。
  4. 服务层将订单信息写入数据库中,并返回成功状态给控制器。
  5. 控制器最终向用户返回成功信息或者错误信息。

这种实现方式主要关注技术实现上的分层,使用了标准的 CRUD 操作(Create、Retrieve、Update、Delete),从而提高了系统的可读性、可维护性和可扩展性。但是,这种实现方式通常比较注重系统的技术方面,而没有很好地考虑到业务逻辑和领域模型。

具体代码:
下面是一个使用常规分层实现编写的订单服务:

@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate ProductService productService;@Autowiredprivate OrderService orderService;@PostMapping("/add")public Result addOrder(@RequestBody OrderRequest request) {// 验证参数合法性if (request.getProductId() == null || request.getUserId() == null || request.getNum() == null) {return Result.error(ErrorCode.PARAMETER_ERROR);}// 查询商品信息Product product = productService.getProductById(request.getProductId());if (product == null) {return Result.error(ErrorCode.PRODUCT_NOT_EXIST);}// 检查库存情况if (product.getStock() < request.getNum()) {return Result.error(ErrorCode.STOCK_NOT_ENOUGH);}// 创建订单Order order = new Order();order.setUserId(request.getUserId());order.setProductId(request.getProductId());order.setNum(request.getNum());order.setTotalFee(product.getPrice().multiply(new BigDecimal(request.getNum())));// 更新库存productService.decreaseStock(request.getProductId(), request.getNum());// 写入数据库orderService.addOrder(order);return Result.success(order);}}

这个服务主要包含了控制器层、业务逻辑层和数据访问层三个部分。其中,控制器层负责接收请求并进行参数验证,业务逻辑层负责处理各种业务逻辑,数据访问层则负责与数据库打交道。

DDD 实现

在 DDD 实现中,我们可能会按照以下步骤进行:

  1. 分析业务场景和业务需求,根据实际情况设计相应的业务模型和领域模型。
  2. 领域模型包含了商品、订单、客户等相关信息,每个模型都有特定的属性和行为。
  3. 根据订单需求,编写相应的方法,例如创建订单、检查库存、更新订单状态等等。
  4. 使用业务逻辑进行订单的创建和处理,通过领域模型来描述和理解业务和系统的本质。
  5. 最后通过前端页面/接口等方式,完成下单操作。

在这种实现方式中,DDD 强调的是业务逻辑和领域模型的建模和设计,更加注重领域专家的意见和业务需求。因此,这种方式能够更加贴近业务需求,具有更好的可扩展性和可维护性。

总之,常用的分层实现方式注重于技术实现和体系结构的设计,DDD 则更注重于业务逻辑和领域模型的设计,让开发人员更容易理解和解决复杂业务问题。

具体代码:

@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate OrderApplicationService orderApplicationService;// 新增订单@PostMapping("/add")public Result addOrder(@RequestBody OrderRequest request) {OrderDto orderDto = orderApplicationService.createOrder(request);return Result.success(orderDto);}}@Service
public class OrderApplicationService {@Autowiredprivate OrderFactory orderFactory;@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate ProductRepository productRepository;// 创建订单并保存订单信息public OrderDto createOrder(OrderRequest request) {// 使用工厂类创建订单对象Order order = orderFactory.createOrder(request);// 检查商品库存是否充足Product product = productRepository.findProductById(request.getProductId());if (product == null) {throw new RuntimeException("Product not exist!");}if (product.getStock() < request.getNum()) {throw new RuntimeException("Stock not enough!");}// 扣减商品库存量product.decreaseStock(request.getNum());// 保存订单信息到数据库orderRepository.saveOrder(order);// 返回订单DTOreturn order.toOrderDto();}}// 订单工厂类,用于创建订单对象
public class OrderFactory {public Order createOrder(OrderRequest request) {// 创建商品对象Product product = new Product(request.getProductId(), request.getNum());// 计算订单总价BigDecimal totalFee = product.getPrice().multiply(new BigDecimal(request.getNum()));// 创建订单对象return new Order(request.getUserId(), product, request.getNum(), totalFee);}}// 订单仓储接口,用于保存订单信息到数据库
public interface OrderRepository {void saveOrder(Order order);}// 商品仓储接口,用于查询商品信息
public interface ProductRepository {Product findProductById(Long productId);}

在这个实现中,我们把业务逻辑和领域模型的设计放到了最重要的位置。 OrderApplicationService 负责调用 OrderFactory 来创建订单对象,并对订单进行一些操作,例如检查库存和扣减库存等事项。OrderFactory 负责创建 Order 对象。而 OrderRepository 和 ProductRepository 则负责数据持久化操作。

可以看出,DDD 的实现方式更加注重领域模型和业务逻辑的设计,尽可能把业务逻辑封装到领域模型里面,提高了代码的可读性和可维护性。同时也让我们更好地理解和解决复杂业务问题。

是怎么把业务逻辑封装到领域模型里面的?

在上面的例子中,订单对象 Order 是一个领域模型。它封装了订单相关的业务逻辑,例如:

  1. 计算订单总价(根据商品单价和数量计算得到)。
  2. 支付订单(将订单状态修改为已支付)。
  3. 取消订单(将订单状态修改为已取消,恢复商品库存等)。

在领域驱动设计中,是将业务逻辑尽可能地放到领域模型里。这样可以让业务逻辑和数据尽量内聚,降低模块之间的耦合度,提高代码的可维护性和可扩展性。

在上面的例子中,通过调用 orderFactory.createOrder(request) 方法创建订单对象;通过调用 order.pay()order.cancel() 方法完成订单的支付和取消操作。

此外,在 OrderApplicationService 服务类中还完成了扣减商品库存、保存订单信息等操作。这些业务逻辑也可以进一步封装到 Order 类的方法或其他领域模型中,以达到领域驱动设计的目标。

总结

粗略的了解一下DDD。可能之前没怎么用过,所以感觉整体还是比较抽象。

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

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

相关文章

js高级 笔记03

目录 01 如何避免全局变量 02 递归函数 03 浅拷贝 04 深拷贝 05 函数的调用模式 06 异步代码数据丢失问题 01 如何避免全局变量 自调用函数 常用于避免全局变量冲突的问题 不同的函数都有一个独立的作用域 所以可以解决命名冲突的问题 自调用函数也叫做沙箱函数 自调用函…

CSS all 属性

CSS all 属性 定义和使用 all 属性用于重置所有属性&#xff0c;除了 unicode-bidi 和 direction。 默认值:none继承:无动画:no。 阅读 animatable版本:CSS3JavaScript 语法:object.style.all“initial” 属性值 值描述initial修改所有元素属性或父元素的值为其初始化值in…

java项目的构建工具-Maven

黑马程序员JavaWeb开发教程 文章目录 一、概述1、介绍&#xff08;1&#xff09;介绍&#xff08;2&#xff09;Maven的作用&#xff08;3&#xff09;官网&#xff08;4&#xff09;仓库 2、安装 二、IDEA 集成 Maven1、配置Maven环境2、创建Maven项目&#xff08;1&#xff0…

入门必备:MySQL本地安装(Windows11)

下载安装 社区版下载地址:https://dev.mysql.com/downloads/ 选择合适自己操作系统的版本&#xff1a; 下载完成后是一个.msi文件&#xff0c;双击安装即可&#xff1b; 接下来请看图&#xff1a; 上面的步骤完成后&#xff0c;大概会弹出黑窗口并且有下面的UI界面&#xff…

Simlab python二次开发1-将所有缸套内表面半径加大1mm

Simlab python二次开发1-将所有缸套内表面半径加大1mm 1、打开模型文件2、getBodiesWithSubString&#xff08;&#xff09;从名字得到Bodies3、建Body类Group3.1、定义放入Group中的Bodies3.2、建Group 4、将缸套内表面建组&#xff0c;并扩半径1mm4.1、simlab.getBodiesFromG…

密码学 | 数字签名 + 数字证书

&#x1f951;原文&#xff1a;数字签名和数字证书的原理解读 - 知乎 &#x1f951;声明&#xff1a;后文图中若未明确指明&#xff0c;默认是 Bob 的公钥或私钥。 Step1&#xff1a;Bob 有两把钥匙&#xff0c;一把是公钥&#xff0c;另一把是私钥。 Step2&#xff1a;Bob 把…

基于Springboot+Vue的Java项目-企业客户管理系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

00_Linux

文章目录 LinuxLinux操作系统的组成Linux的文件系统Linux操作系统中的文件类型Linux操作系统的组织结构 Linux vs WindowsNAT vs 桥接模式 vs 仅主机Linux Shell命令Linux⽂件与⽬录管理相关指令目录文件普通文件文本编辑 用户管理添加用户删除用户用户组管理 文件权限管理权限…

[生活][杂项] 上班党的注意事项

前言 目前是上班已经接近两年了&#xff0c;目前的状态是&#xff0c;一个人租了一个单间在上班。对于这种情况有以下几点需要注意。 钥匙问题&#xff0c;一定不要陷入钥匙丢失的情况&#xff01;一定不要陷入钥匙丢失的情况&#xff01;一定不要陷入钥匙丢失的情况&#xff…

共享电动车哪家强?人民出行共享电动车绿色出行

在当下这个快节奏、高压力的生活环境中,人们越来越注重出行的便捷性和环保性。共享电动车以其灵活方便、绿色低碳的特性,受到了越来越多人的青睐。那么,在众多的共享电动车品牌中,哪个牌子比较好呢?今天,就让我来为大家介绍一下人民出行共享电动车,它以其卓越的品质和贴心的服…

邮箱群组是什么?怎么创建邮箱群组?

在我们群发邮件时&#xff0c;可能会遇到这样的状况&#xff0c;一个个输入邮箱地址效率很低&#xff0c;而且很容易就漏发。而对于一个企业来说&#xff0c;如果出现这样的问题&#xff0c;很有可能会影响公司的业务进展和团队协作。这个时候我们就需要邮箱群组这个功能&#…

【Java】导出Mysql表表结构与注释数据字典

需求&#xff1a; 把mysql中所有表的字段名、数据类型、长度、注释整理成csv&#xff0c;做成数据字典。 import java.io.IOException; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.ResultSet; import ja…

聊聊binlog是什么

1. 上一讲思考題解答:redo日志刷盘策略的选择建议 先给大家解释一下上一讲的思考題&#xff0c;我给大家的一个建议&#xff0c;其实对于redo日志的三种刷盘策略&#xff0c;我们通常建议是设置为1 也就是说&#xff0c;提交事务的时候&#xff0c;redo日志必须是刷入磁盘文件…

AWB学习记录

主要参考食鱼者博客&#xff1a;https://blog.csdn.net/wtzhu_13/article/details/119301096&#xff0c;以及相关的论文&#xff0c;感谢食鱼者老师整理分享。 灰度世界和完全反射 灰度世界法和完全反射法分别是基于(Rmean, Gmean, Bmean)和(Rmax, Gmax, Bmax)来进行白平衡校…

Leetcode 76. 最小覆盖子串和Leetcode 34. 在排序数组中查找元素的第一个和最后一个位置

文章目录 Leetcode 76. 最小覆盖子串题目描述C语言题解和思路解题思路 Leetcode 34. 在排序数组中查找元素的第一个和最后一个位置题目描述C语言题解和思路解题思路 Leetcode 76. 最小覆盖子串 题目描述 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子…

多任务学习的显著优势!

多任务学习是一种机器学习技术&#xff0c;它允许模型同时学习多个相关任务。与不进行多任务的整体学习&#xff08;单任务学习&#xff09;相比&#xff0c;多任务学习具有多个显著优势。 首先&#xff0c;多任务学习可以提高学习效率和速度。在并行学习中&#xff0c;多个任…

数组和指针的联系(C语言)

数组和指针是两种不同的数据类型&#xff0c;数组是一种构造类型&#xff0c;用于存储一组相同类型的变量&#xff1b;而指针是一种特殊类型&#xff0c;专门用来存放数据的地址。数组名除了sizeof(数组名)和&数组名表示整个数组外&#xff0c;其他情况下都表示的是首元素的…

profinet协议基础

文章目录 工业以太网自动化通讯金字塔工业以太网技术比较 profinet概述profinet特性 EtherNet通信EtherCAT通信EtherCat特性EtherCat过程同步 工业以太网 工业以太网是基于IEEE 802.3 (Ethernet)的强大的区域和单元网络。 自动化通讯金字塔 各个组织与工业以太网 工业以太网…

水气表CJ/T188协议学习及实例

水气表CJ/T188协议学习及实例 1 CT/J 188协议简介 CJ/T188协议规定了户用计量仪表(以下简称仪表)&#xff0c;包括水表、燃气表、热量表等仪表数据传输的基本原则&#xff0c;接口形式及物理性能、数据链路、数据标识及数据安全性和数据表达格式的要求。 CJ/T188协议为主-从…

DP10RF001一款工作于200MHz~960MHz低功耗、高性能、单片集成的(G)FSK/OOK无线收发芯片

产品概述. DP10RF001是一款工作于200MHz~960MHz范围内的低功耗、高性能、单片集成的(G)FSK/OOK无线收发机芯片。内部集成完整的射频接收机、射频发射机、频率综合器、调制解调器&#xff0c;只需配备简单、低成本的外围器件就可以获得良好的收发性能。芯片支持灵活可设的数据包…