Wow 2.3.2 发布,让领域驱动设计变得触手可得
基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架
领域驱动 | 事件驱动 | 测试驱动 | 声明式设计 | 响应式编程 | 命令查询职责分离 | 事件源
更新内容 🎉 🎉 🎉
全面支持 OpenAPI 3
- 特性: 全面支持 OpenAPI 3
- 特性: 新增
@CommandRoute.HeaderVariable
支持从请求头提取参数注入命令体 - 特性: 新增
HiddenVariableConverter
支持隐藏命令体中的路由变量 - 特性: 新增
CommandRoute.Method
支持自定义命令路由Method
- 特性: 新增
ScanAggregateHandlerFunction
支持扫描状态聚合API - 依赖: 升级
gradle
版本v8.2.1
- 依赖: 升级
swagger-core
版本v2.2.15
- 特性: 新增
IdsQueryAggregateHandlerFunction
支持根据Ids
查询状态聚合 - 特性: 新增
RouteHandlerFunctionRegistrar
适配wow-openapi
架构图
事件源
可观测性
Spring WebFlux 集成
自动注册 命令 路由处理函数 (
HandlerFunction
) ,开发人员仅需编写领域模型,即可完成服务开发。
测试套件:80%+ 的测试覆盖率轻而易举
Given -> When -> Expect .
前置条件
- 理解 领域驱动设计:《实现领域驱动设计》、《领域驱动设计:软件核心复杂性应对之道》
- 理解 命令查询职责分离(CQRS)
- 理解 事件源架构
- 理解 响应式编程
特性
- Aggregate Modeling
- Single Class
- Inheritance Pattern
- Aggregation Pattern
- Saga Modeling
-
StatelessSaga
-
- Test Suite
- 兼容性测试规范(TCK)
-
AggregateVerifier
-
SagaVerifier
- EventSourcing
- EventStore
- MongoDB (Recommend)
- R2dbc
- Database Sharding
- Table Sharding
- Redis
- Snapshot
- MongoDB
- R2dbc
- Database Sharding
- Table Sharding
- ElasticSearch
- Redis (Recommend)
- EventStore
- 命令等待策略(
WaitStrategy
)-
SENT
: 命令发送成功后发送完成信号 -
PROCESSED
: 命令处理完成后发送完成信号 -
SNAPSHOT
: 快照生成完成后发送完成信号 -
PROJECTED
: 命令产生的事件被投影后发送完成信号
-
- CommandBus
-
InMemoryCommandBus
-
KafkaCommandBus
(Recommend) -
RedisCommandBus
-
LocalFirstCommandBus
-
- DomainEventBus
-
InMemoryDomainEventBus
-
KafkaDomainEventBus
(Recommend) -
RedisDomainEventBus
-
LocalFirstDomainEventBus
-
- StateEventBus
-
InMemoryStateEventBus
-
KafkaStateEventBus
(Recommend) -
RedisStateEventBus
-
LocalFirstStateEventBus
-
- Spring 集成
- Spring Boot Auto Configuration
- Automatically register
CommandAggregate
toRouterFunction
- 可观测性
- OpenTelemetry
- OpenApi
-
WowMetadata
Generator-
wow-compiler
-
Example
测试套件
80%+ 的测试覆盖率轻而易举。
Given -> When -> Expect .
internal class OrderTest { companion object { val SHIPPING_ADDRESS = ShippingAddress("China", "ShangHai", "ShangHai", "HuangPu", "001") } private fun mockCreateOrder(): VerifiedStage<OrderState> { 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.toFlux().filter { it.productId == productId }.map { it.quantity }.last() } } val pricingService = object : PricingService { override fun getProductPrice(productId: String): Mono<BigDecimal> { return orderItems.toFlux().filter { it.productId == productId }.map { it.price }.last() } } return aggregateVerifier<Order, OrderState>(tenantId = tenantId) .inject(DefaultCreateOrderSpec(inventoryService, pricingService)) .given() .`when`(CreateOrder(customerId, orderItems, SHIPPING_ADDRESS)) .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 createOrder() { mockCreateOrder() } /** * 创建订单-库存不足 */ @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.toFlux().filter { it.productId == productId } /* * 模拟库存不足 */ .map { it.quantity - 1 }.last() } } val pricingService = object : PricingService { override fun getProductPrice(productId: String): Mono<BigDecimal> { return orderItems.toFlux().filter { it.productId == productId }.map { it.price }.last() } } aggregateVerifier<Order, OrderState>() .inject(DefaultCreateOrderSpec(inventoryService, pricingService)) .given() .`when`(CreateOrder(customerId, orderItems, SHIPPING_ADDRESS)) /* * 期望:库存不足异常. */ .expectErrorType(InventoryShortageException::class.java) .expectStateAggregate { /* * 该聚合对象处于未初始化状态,即该聚合未创建成功. */ assertThat(it.initialized, equalTo(false)) }.verify() } /** * 创建订单-下单价格与当前价格不一致 */ @Test fun createOrderWhenPriceInconsistency() { 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.toFlux().filter { it.productId == productId }.map { it.quantity }.last() } } val pricingService = object : PricingService { override fun getProductPrice(productId: String): Mono<BigDecimal> { return orderItems.toFlux().filter { it.productId == productId } /* * 模拟下单价格、商品定价不一致 */ .map { it.price.plus(BigDecimal.valueOf(1)) }.last() } } aggregateVerifier<Order, OrderState>() .inject(DefaultCreateOrderSpec(inventoryService, pricingService)) .given() .`when`(CreateOrder(customerId, orderItems, SHIPPING_ADDRESS)) /* * 期望:价格不一致异常. */ .expectErrorType(PriceInconsistencyException::class.java).verify() } private fun mockPayOrder(): VerifiedStage<OrderState> { val verifiedStageAfterCreateOrder = mockCreateOrder() val previousState = verifiedStageAfterCreateOrder.stateRoot val payOrder = PayOrder( previousState.id, GlobalIdGenerator.generateAsString(), previousState.totalAmount ) return verifiedStageAfterCreateOrder .then() .given() /* * 2. 当接收到命令 */ .`when`(payOrder) /* * 3.1 期望将会产生1个事件 */ .expectEventCount(1) /* * 3.2 期望将会产生一个 OrderPaid 事件 (3.1 可以不需要) */ .expectEventType(OrderPaid::class.java) /* * 3.3 期望产生的事件状态 */ .expectEventBody<OrderPaid> { assertThat(it.amount, equalTo(payOrder.amount)) } /* * 4. 期望当前聚合状态 */ .expectState { assertThat(it.address, equalTo(SHIPPING_ADDRESS)) assertThat(it.paidAmount, equalTo(payOrder.amount)) assertThat(it.status, equalTo(OrderStatus.PAID)) } /* * 完成测试编排后,验证期望. */ .verify() } /** * 支付订单 */ @Test fun payOrder() { mockPayOrder() } /** * 支付订单-超付 */ @Test fun payOrderWhenOverPay() { val verifiedStageAfterCreateOrder = mockCreateOrder() val previousState = verifiedStageAfterCreateOrder.stateRoot val payOrder = PayOrder( previousState.id, GlobalIdGenerator.generateAsString(), previousState.totalAmount.plus( BigDecimal.valueOf(1) ) ) verifiedStageAfterCreateOrder .then() .given() /* * 2. 处理 PayOrder 命令 */ .`when`(payOrder) /* * 3.1 期望将会产生俩个事件分别是: OrderPaid、OrderOverPaid */ .expectEventType(OrderPaid::class.java, OrderOverPaid::class.java) /* * 3.2 期望产生的事件状态 */ .expectEventStream { val itr = it.iterator() /* * OrderPaid */ val orderPaid = itr.next().body as OrderPaid assertThat(orderPaid.paid, equalTo(true)) /* * OrderOverPaid */ val orderOverPaid = itr.next().body as OrderOverPaid assertThat( orderOverPaid.overPay, equalTo(payOrder.amount.minus(previousState.totalAmount)) ) } /* * 4. 期望当前聚合状态 */ .expectState { assertThat(it.paidAmount, equalTo(previousState.totalAmount)) assertThat(it.status, equalTo(OrderStatus.PAID)) } .verify() } /** * 发货 */ @Test fun ship() { val verifiedStageAfterPayOrder = mockPayOrder() val shipOrder = ShipOrder(verifiedStageAfterPayOrder.stateRoot.id) verifiedStageAfterPayOrder .then().given() .`when`(shipOrder) .expectEventType(OrderShipped::class.java) /* * 4. 期望当前聚合状态 */ .expectState { assertThat(it.status, equalTo(OrderStatus.SHIPPED)) } .verify() } @Test fun shipGivenUnpaid() { val verifiedStageAfterCreateOrder = mockCreateOrder() val shipOrder = ShipOrder(verifiedStageAfterCreateOrder.stateRoot.id) verifiedStageAfterCreateOrder.then().given() .`when`(shipOrder) .expectErrorType(IllegalStateException::class.java) .expectState { /* * 验证聚合状态[未]发生变更. */ assertThat(it.paidAmount, equalTo(BigDecimal.ZERO)) assertThat(it.status, equalTo(OrderStatus.CREATED)) } .verify() } private fun mockDeleteOrder(): VerifiedStage<OrderState> { val verifiedStageAfterCreateOrder = mockCreateOrder() return verifiedStageAfterCreateOrder.then().given() .`when`(DeleteAggregate) .expectEventType(AggregateDeleted::class.java) .expectStateAggregate { assertThat(it.deleted, equalTo(true)) } .verify() } @Test fun deleteOrder() { mockDeleteOrder() } @Test fun deleteGivenDeleted() { val verifiedStageAfterDelete = mockDeleteOrder() verifiedStageAfterDelete.then().given() .`when`(DeleteAggregate) .expectErrorType(IllegalAccessDeletedAggregateException::class.java) .expectError<IllegalAccessDeletedAggregateException> { assertThat(it.aggregateId, equalTo(verifiedStageAfterDelete.stateAggregate.aggregateId)) }.expectStateAggregate { assertThat(it.deleted, equalTo(true)) } .verify() } }
设计
聚合建模
Single Class | Inheritance Pattern | Aggregation Pattern |
---|---|---|
加载聚合
聚合状态流
发送命令
命令与事件流
Saga - OrderProcessManager (Demo)

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
苞米豆-多数据源 4.1.1 发布:适配 spring boot3
dynamic-datasource 是一款极其简单的基于 spring-boot 的多数据源组件,可以方便快速的让你的系统支持多数据源。 https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter 特性 支持数据源分组,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。 支持数据库敏感配置信息加密ENC()。 支持每个数据库独立初始化表结构 schema 和数据库 database。 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。 支持自定义注解,需继承 DS (3.2.0+)。 提供并简化对 Druid,HikariCp,BeeCp,Dbcp2 的快速集成。 提供对 Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi 等组件的集成方案。 提供自定义数据源来源方案(如全从数据库加载)。 提供项目启动后动态增加移除数据源方案。 提供 Mybatis 环境下的纯读写分离方案。 提供使用spel 动态参数解析数据源方案。内置 spel,session,header,...
- 下一篇
🎉 Laravel + Vue3 前后端分离后端框架 CatchAdmin v3.1.7 发布,
介绍 CatchAdmin是一款基于Laravel和Element Plus二次开发而成后台管理系统。Laravel社区也有许多非常优秀的后台管理系统,例如Nova, 官方出品,当然是收费的,免费的有基于Livewire的Filament,还有不得不说的Laravel Admin。CatchAdmin还是采用传统的前后端分离策略,Laravel框架仅仅作为Api输出。将管理系统模块之间的耦合降到了最低限度。每个模块之间都有独立的控制器,路由,模型,数据表。在开发上尽可能将模块之间的影响降到最低,降低了开发上的难度。基于CatchAdmin可以开发CMS,CRM,OA等 等系统。也封装了很多实用的工具,提升开发体验。 V3.1.7 日志 新增 Excel 导入 新增 Excel 导出 新增文件下载 hook 修复 migration 表存在报错 新增用户导出 优化前端 http 请求 优化前端文件下载 hook 等等其他.... 视频 catchadmin 模块创建 catchadmin 之快速开发 功能 用户管理 后台用户管理 部门管理 配置公司的部门结构,支持树形结构 岗位管理 配...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS8编译安装MySQL8.0.19
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS8编译安装MySQL8.0.19
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- MySQL8.0.19开启GTID主从同步CentOS8