DDD学习

news/2025/2/5 20:42:59/

概述

学习一下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.ppmy.cn/news/1423406.html

相关文章

京西商城——前端项目的创建以及前后端联调

创建VUE项目 在jingxi_shop_project文件夹中再创建一个 frontend 文件夹用来存放前端项目 /jingxi_shop_project/backend/jingxi_shop_project....../frontend/jingxi_shop_web......首先要安装 node.js 和 VUE cli&#xff0c;进入到项目目录内创建项目 vue create jingxi_…

2024 年 Web 前端开发趋势

希腊哲学家赫拉克利特认为&#xff0c;变化是生命中唯一不变的东西。这句话适用于我们的个人生活、行业和职业领域。 尤其是前端开发领域&#xff0c;新技术、开发趋势、库和框架不断涌现&#xff0c;变化并不陌生。最近发生的一些事件正在改变开发人员构建网站和 Web 应用的方…

前后端系统开发之——文章管理

原文地址&#xff1a;前后端系统开发之——文章管理 - Pleasure的博客 下面是正文内容&#xff1a; 前言 主要使用的技术&#xff1a;前端使用的是Vue.js&#xff0c;后端使用的是SpringBoot。如不雷同可以直接跳过了。 文章管理是这个系统最主要的一个功能也是最常规的一个功…

202462读书笔记|《一世珍藏的诗歌200首》——你曾经羞赧地向我问起, 是谁最早在此留下足印

202462读书笔记|《一世珍藏的诗歌200首》——你曾经羞赧地向我问起&#xff0c; 是谁最早在此留下足印 《一世珍藏的诗歌200首》作者金宏宇&#xff0c;很多美好的诗&#xff0c;有徐志摩&#xff0c;戴望舒&#xff0c;林徽因&#xff0c;舒婷等的诗精选&#xff0c;很值得一读…

Python分析之3 种空间插值方法

插值是一个非常常见的数学概念,不仅数据科学家使用它,而且各个领域的人们也使用它。然而,在处理地理空间数据时,插值变得更加复杂,因为您需要基于几个通常稀疏的观测值创建代表性网格。 在深入研究地理空间部分之前,让我们简要回顾一下线性插值。 为了演示的目的,我将使…

FebHost:谁可以注册.CA加拿大域名?

在加拿大&#xff0c;互联网域名的注册管理遵循一套独特的规则。特别是对于代表加拿大身份的顶级域名“.ca”&#xff0c;其申请和注册过程涉及一些严格的条件。这些条件确保了只有符合特定标准的个人或实体才能获得这一具有国家象征意义的网络地址。 首先&#xff0c;想要注册…

算法学习笔记Day6——二维数组的花式遍历

引言&#xff1a;一些操作的最佳算法可能不符合我们的直观思路&#xff0c;但是它对计算机来说是有效的&#xff0c;对于我们来说匪夷所思、八竿子打不着的操作可能正好就能够得出符合要求的操作。这其中有很深的数学变换思维&#xff0c;需要勤于思考&#xff0c;多加练习。 …

C++ | Leetcode C++题解之第23题合并K个升序链表

题目: 题解&#xff1a; class Solution {// 21. 合并两个有序链表ListNode *mergeTwoLists(ListNode *list1, ListNode *list2) {auto dummy new ListNode(); // 用哨兵节点简化代码逻辑auto cur dummy; // cur 指向新链表的末尾while (list1 && list2) {if (list1…