领域驱动设计(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接口,以避免产生“莫须有”的错误。阁下该如何破解此问题! 这里,直接给出一种通用的方法,…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...

Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...