🎉 领域模型即服务 | Wow 2.12.1 发布
基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架
领域驱动 | 事件驱动 | 测试驱动 | 声明式设计 | 响应式编程 | 命令查询职责分离 | 事件溯源
更新内容
增强 事件补偿控制台
- 特性(
dashboard
): 支持在事件补偿控制台配置执行失败的重试策略 - 特性(
dashboard
): 支持查看事务补偿-历史记录 - 特性(
dashboard
): 添加事件补偿-正在执行导航 - 特性(
dashboard
): 添加事件补偿通知快速打开执行失败链接 - 特性(
OpenAPI
): 添加SnapshotConverter
/StateEventConverter
/EventStreamConverter
/DomainEventConverter
以适配 Wow 内置序列化器 - OpenAPI-Schema - 特性(
CommandGateawy
): 添加sendAndWaitForSnapshot
API - 依赖: 升级
org.springframework.boot:spring-boot-dependencies
v3.2.1
- 依赖: 升级
org.jetbrains.kotlin.kapt
v1.9.22
- 依赖: 升级
angular
v17.0.8
事件补偿控制台
快速开始
使用 Wow 项目模板快速创建基于 Wow 框架的 DDD 项目。
特性概览
架构图
性能测试 (Example)
- 测试代码:Example
- 测试场景:加入购物车、下单
- 命令发送等待模式(
WaitStrategy
):SENT
、PROCESSED
部署
测试报告
加入购物车
命令等待策略(
WaitStrategy
)为SENT
模式,加入购物车命令(AddCartItem
)写请求 API 经过 2 分钟的压测,平均 TPS 为 59625,峰值为 82312,平均响应时间为 29 毫秒。
命令等待策略(
WaitStrategy
)为PROCESSED
模式,加入购物车命令(AddCartItem
)写请求 API 经过 2 分钟的压测,平均 TPS 为 18696,峰值为 24141,平均响应时间为 239 毫秒。
下单
命令等待策略(
WaitStrategy
)为SENT
模式,下单命令(CreateOrder
)写请求 API 经过 2 分钟的压测,平均 TPS 为 47838,峰值为 86200,平均响应时间为 217 毫秒。
命令等待策略(
WaitStrategy
)为PROCESSED
模式,下单命令(CreateOrder
)写请求 API 经过 2 分钟的压测,平均 TPS 为 18230,峰值为 25506,平均响应时间为 268 毫秒。
事件源
可观测性
OpenAPI (Spring WebFlux 集成)
自动注册 命令 路由处理函数 (
HandlerFunction
) ,开发人员仅需编写领域模型,即可完成服务开发。
测试套件:80%+ 的测试覆盖率轻而易举
Given -> When -> Expect .
前置条件
- 理解 领域驱动设计:《实现领域驱动设计》、《领域驱动设计:软件核心复杂性应对之道》
- 理解 命令查询职责分离(CQRS)
- 理解 事件源架构
- 理解 响应式编程
Example
订单服务(Kotlin)
银行转账(JAVA)
单元测试套件
80%+ 的测试覆盖率轻而易举。
Given -> When -> Expect .
Aggregate Unit Test (AggregateVerifier
)
internal class OrderTest { @Test private fun createOrder() { val tenantId = GlobalIdGenerator.generateAsString() val customerId = GlobalIdGenerator.generateAsString() val orderItem = OrderItem( GlobalIdGenerator.generateAsString(), GlobalIdGenerator.generateAsString(), BigDecimal.valueOf(10), 10, ) val orderItems = listOf(orderItem) val inventoryService = object : InventoryService { override fun getInventory(productId: String): Mono<Int> { return orderItems.filter { it.productId == productId }.map { it.quantity }.first().toMono() } } val pricingService = object : PricingService { override fun getProductPrice(productId: String): Mono<BigDecimal> { return orderItems.filter { it.productId == productId }.map { it.price }.first().toMono() } } aggregateVerifier<Order, OrderState>(tenantId = tenantId) .inject(DefaultCreateOrderSpec(inventoryService, pricingService)) .given() .`when`(CreateOrder(customerId, orderItems, SHIPPING_ADDRESS, false)) .expectEventCount(1) .expectEventType(OrderCreated::class.java) .expectStateAggregate { assertThat(it.aggregateId.tenantId, equalTo(tenantId)) } .expectState { assertThat(it.id, notNullValue()) assertThat(it.customerId, equalTo(customerId)) assertThat(it.address, equalTo(SHIPPING_ADDRESS)) assertThat(it.items, equalTo(orderItems)) assertThat(it.status, equalTo(OrderStatus.CREATED)) } .verify() } @Test fun createOrderGivenEmptyItems() { val customerId = GlobalIdGenerator.generateAsString() aggregateVerifier<Order, OrderState>() .inject(mockk<CreateOrderSpec>(), "createOrderSpec") .given() .`when`(CreateOrder(customerId, listOf(), SHIPPING_ADDRESS, false)) .expectErrorType(IllegalArgumentException::class.java) .expectStateAggregate { /* * 该聚合对象处于未初始化状态,即该聚合未创建成功. */ assertThat(it.initialized, equalTo(false)) }.verify() } /** * 创建订单-库存不足 */ @Test fun createOrderWhenInventoryShortage() { val customerId = GlobalIdGenerator.generateAsString() val orderItem = OrderItem( GlobalIdGenerator.generateAsString(), GlobalIdGenerator.generateAsString(), BigDecimal.valueOf(10), 10, ) val orderItems = listOf(orderItem) val inventoryService = object : InventoryService { override fun getInventory(productId: String): Mono<Int> { return orderItems.filter { it.productId == productId } /* * 模拟库存不足 */ .map { it.quantity - 1 }.first().toMono() } } val pricingService = object : PricingService { override fun getProductPrice(productId: String): Mono<BigDecimal> { return orderItems.filter { it.productId == productId }.map { it.price }.first().toMono() } } aggregateVerifier<Order, OrderState>() .inject(DefaultCreateOrderSpec(inventoryService, pricingService)) .given() .`when`(CreateOrder(customerId, orderItems, SHIPPING_ADDRESS, false)) /* * 期望:库存不足异常. */ .expectErrorType(InventoryShortageException::class.java) .expectStateAggregate { /* * 该聚合对象处于未初始化状态,即该聚合未创建成功. */ assertThat(it.initialized, equalTo(false)) }.verify() } }
Saga Unit Test (SagaVerifier
)
class CartSagaTest { @Test fun onOrderCreated() { val orderItem = OrderItem( GlobalIdGenerator.generateAsString(), GlobalIdGenerator.generateAsString(), BigDecimal.valueOf(10), 10, ) sagaVerifier<CartSaga>() .`when`( mockk<OrderCreated> { every { customerId } returns "customerId" every { items } returns listOf(orderItem) every { fromCart } returns true }, ) .expectCommandBody<RemoveCartItem> { assertThat(it.id, equalTo("customerId")) assertThat(it.productIds, hasSize(1)) assertThat(it.productIds.first(), equalTo(orderItem.productId)) } .verify() } }
设计
聚合建模
Single Class | Inheritance Pattern | Aggregation Pattern |
---|---|---|
加载聚合
聚合状态流
发送命令
命令与事件流
事件补偿
用例图
执行时序图
Dashboard

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
从容器的发展历史理解容器的本质
本文分享自华为云社区《容器化学习——从容器的发展历史理解容器的本质》,作者:breakDawn 。 近期工作上开始接触了相关容器化的内容,因此整理学习了一堆有关容器化的知识,特此进行分享。 首先,理解K8S和容器,首先需要学习以下它的发展历史,才能逐步理解容器的意义和作用。 阶段一:隔离文件——chroot命令的诞生 在1979年,Unix系统引入了一个革命性的命令,它允许系统管理员将进程的根目录锁定在指定的位置,从而有效地限制了该进程访问的文件系统范围。这个命令成为了早期容器技术的基石,因为它实现了基本的文件系统隔离,确保进程不能访问其指定根目录之外的任何文件或目录。 这种隔离能力对于安全性至关重要,特别是在监控潜在的恶意活动时。通过创建一个隔离的环境,或所谓的“黑盒”,系统管理员能够更安全地运行和监控可疑的代码或程序。 因此这个以文件形式进行隔离的命令为现代容器技术奠定了一个重要的思想基础:隔离。 后续的很多演变也都是基于“隔离”进行变化。 阶段二: 隔离访问——namespace名称空间 在2002年,Linux社区迎来了一个重要的里程碑:引入了Linux名称空间(namespa...
- 下一篇
MooTool 1.5.8 发布,开发者常备小工具
MooTool 1.5.8 已发布,这是一个用 Java 编写的开发者常备桌面小工具集。 更新内容 新增文件格式化工具、PDF拆分/合并工具 by:felixnan168 设置:新增使用习惯-功能面板位置设置 主题:修复主题切换后,系统托盘样式不跟随更新的问题 全局:更多强调色设置 随手记:outline强调色跟随笔记设置 随手记:增加搜索框 Java Console编辑器语法高亮 下载地址:https://gitee.com/zhoubochina/MooTool/releases
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8编译安装MySQL8.0.19
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Red5直播服务器,属于Java语言的直播服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7