领域驱动设计(DDD)四 订单管理系统实践步骤
以下是基于 领域驱动设计(DDD) 的订单管理系统实践步骤,系统功能主要包括订单的创建、更新、查询和状态管理,采用 Spring Boot 框架进行实现。
1. 需求分析
订单管理系统的基本功能:
- 订单创建:用户下单创建订单。
- 订单状态更新:更新订单状态(如已支付、已发货、已完成)。
- 订单查询:支持按订单 ID 或用户查询订单信息。
- 库存管理(简化版):订单创建时检查并扣减库存。
2. 领域划分与限界上下文
限界上下文
- 订单上下文(Order Context):负责订单的生命周期管理。
- 库存上下文(Inventory Context):负责商品库存的管理。
模块划分
src/main/java/com/example/order
├── application # 应用层:处理用例逻辑
│ ├── service # 应用服务
│ └── dto # 数据传输对象
├── domain # 领域层:业务逻辑
│ ├── model # 领域模型(实体、值对象、聚合)
│ ├── repository # 仓储接口
│ ├── service # 领域服务
│ └── event # 领域事件
├── infrastructure # 基础设施层:技术实现
│ ├── repository # 仓储实现
│ └── event # 事件机制
└── interfaces # 接口层:用户交互├── controller # REST 接口└── vo # 视图对象
3. 代码实现
3.1 领域层设计
领域模型
(1) 订单实体
public class Order {private Long id; // 订单IDprivate String customerId; // 用户IDprivate List<OrderItem> items; // 订单项private OrderStatus status; // 订单状态// 创建订单public Order(String customerId, List<OrderItem> items) {this.customerId = customerId;this.items = items;this.status = OrderStatus.CREATED;}// 更新订单状态public void updateStatus(OrderStatus status) {if (this.status.canTransitionTo(status)) {this.status = status;} else {throw new IllegalStateException("非法状态转换");}}// 计算总金额public BigDecimal calculateTotal() {return items.stream().map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()))).reduce(BigDecimal.ZERO, BigDecimal::add);}// Getters and Setters
}
(2) 订单项值对象
public class OrderItem {private Long productId; // 商品IDprivate int quantity; // 数量private BigDecimal price; // 单价// 构造方法public OrderItem(Long productId, int quantity, BigDecimal price) {this.productId = productId;this.quantity = quantity;this.price = price;}// Getters and Setters
}
(3) 订单状态值对象
public enum OrderStatus {CREATED, PAID, SHIPPED, COMPLETED;// 状态转换规则public boolean canTransitionTo(OrderStatus newStatus) {return switch (this) {case CREATED -> newStatus == PAID;case PAID -> newStatus == SHIPPED;case SHIPPED -> newStatus == COMPLETED;default -> false;};}
}
3.2 应用层设计
订单服务
@Service
public class OrderApplicationService {private final OrderRepository orderRepository;private final InventoryService inventoryService;public OrderApplicationService(OrderRepository orderRepository, InventoryService inventoryService) {this.orderRepository = orderRepository;this.inventoryService = inventoryService;}// 创建订单public Long createOrder(String customerId, List<OrderItemDto> itemDtos) {// 转换 DTO 为领域模型List<OrderItem> items = itemDtos.stream().map(dto -> new OrderItem(dto.getProductId(), dto.getQuantity(), dto.getPrice())).toList();// 检查库存items.forEach(item -> inventoryService.reserveStock(item.getProductId(), item.getQuantity()));// 创建订单Order order = new Order(customerId, items);orderRepository.save(order);return order.getId();}// 更新订单状态public void updateOrderStatus(Long orderId, OrderStatus status) {Order order = orderRepository.findById(orderId).orElseThrow(() -> new RuntimeException("订单不存在"));order.updateStatus(status);orderRepository.save(order);}// 查询订单public OrderDto getOrderById(Long orderId) {Order order = orderRepository.findById(orderId).orElseThrow(() -> new RuntimeException("订单不存在"));return OrderDto.fromDomain(order);}
}
3.3 基础设施层设计
仓储实现
@Repository
public class JpaOrderRepository implements OrderRepository {private final SpringDataOrderJpaRepository jpaRepository;public JpaOrderRepository(SpringDataOrderJpaRepository jpaRepository) {this.jpaRepository = jpaRepository;}@Overridepublic void save(Order order) {jpaRepository.save(order);}@Overridepublic Optional<Order> findById(Long id) {return jpaRepository.findById(id);}
}
事件机制
@Component
public class DomainEventPublisher {private final ApplicationEventPublisher eventPublisher;public DomainEventPublisher(ApplicationEventPublisher eventPublisher) {this.eventPublisher = eventPublisher;}public void publish(Object event) {eventPublisher.publishEvent(event);}
}
3.4 接口层设计
订单控制器
@RestController
@RequestMapping("/api/orders")
public class OrderController {private final OrderApplicationService orderService;public OrderController(OrderApplicationService orderService) {this.orderService = orderService;}@PostMappingpublic ResponseEntity<Long> createOrder(@RequestBody OrderRequest request) {Long orderId = orderService.createOrder(request.getCustomerId(), request.getItems());return ResponseEntity.status(HttpStatus.CREATED).body(orderId);}@PutMapping("/{orderId}/status")public ResponseEntity<Void> updateStatus(@PathVariable Long orderId, @RequestParam OrderStatus status) {orderService.updateOrderStatus(orderId, status);return ResponseEntity.ok().build();}@GetMapping("/{orderId}")public ResponseEntity<OrderDto> getOrderById(@PathVariable Long orderId) {return ResponseEntity.ok(orderService.getOrderById(orderId));}
}
3.5 示例数据传输对象(DTO)
public class OrderDto {private Long id;private String customerId;private List<OrderItemDto> items;private OrderStatus status;public static OrderDto fromDomain(Order order) {return new OrderDto(order.getId(), order.getCustomerId(),order.getItems().stream().map(OrderItemDto::fromDomain).toList(),order.getStatus());}
}
4. 运行与测试
数据库表结构(MySQL 示例)
CREATE TABLE orders (id BIGINT AUTO_INCREMENT PRIMARY KEY,customer_id VARCHAR(255),status VARCHAR(50)
);CREATE TABLE order_items (id BIGINT AUTO_INCREMENT PRIMARY KEY,order_id BIGINT,product_id BIGINT,quantity INT,price DECIMAL(10,2),FOREIGN KEY (order_id) REFERENCES orders(id)
);
测试示例
-
创建订单
POST /api/orders {"customerId": "123","items": [{ "productId": 1, "quantity": 2, "price": 50.00 },{ "productId": 2, "quantity": 1, "price": 100.00 }] } -
更新订单状态
PUT /api/orders/1/status?status=PAID -
查询订单
GET /api/orders/1
这套设计是 DDD 的小型实践示例,后续可以扩展领域事件、复杂聚合和限界上下文的实现。
相关文章:
领域驱动设计(DDD)四 订单管理系统实践步骤
以下是基于 领域驱动设计(DDD) 的订单管理系统实践步骤,系统功能主要包括订单的创建、更新、查询和状态管理,采用 Spring Boot 框架进行实现。 1. 需求分析 订单管理系统的基本功能: 订单创建:用户下单创…...
leetcode 面试经典 150 题:简化路径
链接简化路径题序号71题型字符串解法栈难度中等熟练度✅✅✅ 题目 给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 ‘/’ 开头),请你将其转化为 更加简洁的规范路径。 在 Unix 风格的文件系统中规则如下…...
基于 STM32 的智能农业温室控制系统设计
1. 引言 随着农业现代化的发展,智能农业温室控制系统对于提高农作物产量和质量具有重要意义。该系统能够实时监测温室内的环境参数,如温度、湿度、光照强度和土壤湿度等,并根据这些参数自动调节温室设备,如通风扇、加热器、加湿器…...
【Spring Boot】掌握 Spring 事务:隔离级别与传播机制解读与应用
前言 🌟🌟本期讲解关于spring 事务传播机制介绍~~~ 🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客 🔥 你的点赞就是小编不断更新的最大动力 🎆那么废话…...
【Postgres_Python】使用python脚本将多个PG数据库合并为一个PG数据库
需要合并的多个PG数据库表个数和结构一致,这里提供一种思路,选择sql语句insert插入的方式进行,即将其他PG数据库的每个表内容插入到一个PG数据库中完成数据库合并 示例代码说明: 选择一个数据库导出表结构为.sql文件(…...
Tailwind CSS v4.0 发布
Holy shit its actually done ! 1 月 22 日,Tailwind CSS 正式发布了 4.0 版本,针对性能和灵活性进行了优化,重新构想了配置和定制体验,并充分利用了 Web 平台提供的最新进展。 新的高性能引擎- 完整构建速度提高 5 …...
pandas基础:文件的读取和写入
文件的读取和写入 读取csv文件 csv文件: name,age,city Alice,25,New York Bob,30,Los Angelesread_csv(filename) header:如 何处理文件的第一行。header0将第一行作为列名,headerNone表示文件中没有列名,所有行都是数据。 im…...
【MySQL — 数据库增删改查操作】深入解析MySQL的create insert 操作
数据库CRUD操作 1 CRUD简介 CURD是对数据库中的记录进行基本的增删改查操作: 2. Create 新增 语法 INSERT [INTO] table_name[(column [,column] ...)] VALUES(value_list)[,(value_list)] ... # value 后面的列的个数和类型,要和表结构匹配…...
每日OJ_牛客_小红的子串_滑动窗口+前缀和_C++_Java
目录 牛客_小红的子串_滑动窗口前缀和 题目解析 C代码 Java代码 牛客_小红的子串_滑动窗口前缀和 小红的子串 描述: 小红拿到了一个长度为nnn的字符串,她准备选取一段子串,满足该子串中字母的种类数量在[l,r]之间。小红想知道&…...
HTTP 配置与应用(局域网)
想做一个自己学习的有关的csdn账号,努力奋斗......会更新我计算机网络实验课程的所有内容,还有其他的学习知识^_^,为自己巩固一下所学知识,下次更新HTTP 配置与应用(不同网段)。 我是一个萌新小白…...
ultralytics 是什么?
ultralytics 是一个用于计算机视觉任务的 Python 库,专注于提供高效、易用的目标检测、实例分割和图像分类工具。它最著名的功能是实现 YOLO(You Only Look Once) 系列模型,特别是最新的 YOLOv8。 1. YOLO 是什么? YO…...
AI竞争:从技术壁垒到用户数据之争
标题:AI竞争:从技术壁垒到用户数据之争 文章信息摘要: AI市场呈现开放模型与封闭模型并存的双轨发展态势,但核心竞争力已从模型技术转向用户数据积累和使用习惯培养。商业模式正在多元化发展,从早期的价格战转向subsc…...
MySQL 主从复制(单组传统复制,GTID复制。双主复制)
案例环境 单组复制 master: 192.168.180.143 slave01:192.168.180.144 双组复制 master01:192.168.180.143 master02:192.168.180.144 案例过程 准备工作 关闭所有防火墙 setenforce 0 && systemctl stop firewa…...
python学opencv|读取图像(四十)掩模:三通道图像的局部覆盖
【1】引言 前序学习了使用numpy创建单通道的灰色图像,并对灰色图像的局部进行了颜色更改,相关链接为: python学opencv|读取图像(九)用numpy创建黑白相间灰度图_numpy生成全黑图片-CSDN博客 之后又学习了使用numpy创…...
vue3 中如何监听 props 中的值的变化
在 Vue 3 中,你可以使用 watch 函数来监听组件的 props 值的变化。watch 函数允许你观察一个或多个响应式数据源,并在这些数据源发生变化时执行回调函数。 以下是一个示例,展示了如何在 Vue 3 中使用 watch 来监听 props 中的值的变化&#…...
Scrapy之一个item包含多级页面的处理方案
目标 在实际开发过程中,我们所需要的数据往往需要通过多个页面的数据汇总得到,通过列表获取到的数据只有简单的介绍。站在Scrapy框架的角度来看,实际上就是考虑如何处理一个item包含多级页面数据的问题。本文将以获取叶子猪网站的手游排行榜及…...
hive 自动检测、自动重启、记录检测日志、自动清理日志
最终效果 定时检测hive运行状态,进程不存在或者进程存在但是不监听端口的hiveserver2,自动重新拉起每次检测脚本执行的日志都会保存在log目录下.check文件,每一个月一个文件每月15日,删除2月前的检测日志开启hive自带日志输出后&…...
HFSS同轴替换波端口
波端口仿真正常 将波端口换成内径内径0.3mm外径0.6mm同轴之后 结果很不对 换成下面的尺寸就好了...
【2024年华为OD机试】 (C卷,100分)- 素数之积(JavaScriptJava PythonC/C++)
一、问题描述 RSA 因数分解问题 题目描述 RSA 加密算法在网络安全世界中无处不在,它利用了极大整数因数分解的困难度。数据越大,安全系数越高。给定一个 32 位正整数,请对其进行因数分解,找出是哪两个素数的乘积。 输入描述 …...
【C++模板】:如何判断自定义类型是否实现某个函数
一、引子 偶尔我们会面对这样的尴尬的场景,我们需要显示的去判断在某个自定义类型中,是否已经提供了我们期待的API接口,以避免产生“莫须有”的错误。阁下该如何破解此问题! 这里,直接给出一种通用的方法,…...
基于2D工程图几何特征与梯度提升模型的制造成本智能预测
1. 项目概述:从图纸到报价的智能革命在制造业,尤其是像汽车零部件这样的离散制造领域,报价速度直接决定了订单的生死。传统上,拿到一张新的2D工程图(DWG格式),成本工程师需要花上几天甚至几周时…...
ARM PMU性能监控单元原理与实践指南
1. ARM PMU性能监控单元概述性能监控单元(PMU)是现代ARM处理器中用于硬件级性能分析的核心组件。它通过一组可编程的硬件计数器,实现对处理器内部各种关键事件的精确测量。这些事件涵盖了从指令执行、缓存访问到内存子系统行为等处理器活动的…...
CentOS服务器上VNC连接失败?手把手教你排查并修复个人端口问题(附重启命令)
CentOS服务器VNC连接故障深度排查指南:从原理到实战当你在深夜赶项目时,突然发现VNC连接不上服务器,那种焦虑感我深有体会。去年参与半导体器件仿真项目时,我也曾被这个问题困扰整整两天。本文将分享一套经过实战检验的排查方法论…...
举一个具体例子说明为什么索引不是越多越好,举具体字段
文章目录1. 核心舞台:笔记表 (t_note) 结构设计🚨 错误的操作:2. 结合具体字段,拆解三大翻车现场现场一:给 view_count(浏览量)加索引 —— 导致写放大,拖垮数据库现场二:…...
LVGL多页面开发避坑:用内部Timer替代轮询,解决页面切换时的内存踩踏问题
LVGL多页面开发中的内存安全实践:用Timer机制替代轮询的工程解决方案 在嵌入式UI开发中,LVGL因其轻量级和跨平台特性成为热门选择。但当项目复杂度提升到多页面交互时,开发者往往会遇到一个棘手问题:如何在频繁切换页面的同时保证…...
Claude端到端测试设计终极清单:覆盖17类非功能需求(含延迟敏感度分级、幻觉熔断阈值、多轮对话状态持久化验证)
更多请点击: https://kaifayun.com 第一章:Claude端到端测试设计的演进逻辑与核心范式 Claude端到端测试并非静态产物,而是随模型能力边界拓展、交互场景复杂化及可靠性要求升级而持续演化的工程实践。其演进逻辑根植于三个关键张力…...
LoRa物联网与动态基线算法在养殖体温监测中的实战应用
1. 项目概述:为什么我们需要一个智能体温监测系统?在规模化养殖场里干了十几年,我见过太多因为体温异常没被及时发现而导致的损失。一头育肥猪突然不吃食,等饲养员第二天巡栏发现时,可能已经高烧好几天,继发…...
昇腾CANN cmake 实战:CANN CMake 构建系统——跨平台编译配置与模块化管理
8 个 CANN 仓库各需独立构建(ops-transformer/ops-nn/hccl/ge/…)→ 手写 8 套 CMakeLists.txt(CANN 路径判断、跨 NPU 型号编译、第三方库兼容)。cmake 仓库提供统一的 FindCANN.cmake CANNConfig.cmake 模板——任何仓库只需 f…...
SMUDebugTool:AMD Ryzen处理器深度调试与性能调优完全指南
SMUDebugTool:AMD Ryzen处理器深度调试与性能调优完全指南 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https:…...
告别手动标注!用SAM(Segment Anything)和Python脚本,5分钟批量生成你的分割数据集
5分钟批量生成分割数据集:SAM自动化标注全流程实战 在计算机视觉领域,数据标注一直是制约模型开发效率的瓶颈。传统手工标注不仅耗时费力,还容易引入人为误差。Meta开源的Segment Anything Model(SAM)彻底改变了这一局…...
