Wow 2.2.1 发布,让领域驱动设计变得触手可得
基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架
领域驱动 | 事件驱动 | 测试驱动 | 声明式设计 | 响应式编程 | 命令查询职责分离 | 事件源
更新内容 🎉 🎉 🎉
- 特性: Wow 编译器(
wow-compiler
)支持检测聚合模型超类静态租户ID - 特性: 新增
RecordEventFunctionErrorHandler
支持记录事件函数处理异常,以支持更友好的事件补偿 - 特性:
AggregateVerifier
支持尽早检测状态聚合模型序列化问题 - 特性:
CommandHandlerFunction
支持从Http.Header
中获取聚合ID
架构图
事件源
可观测性
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业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
盘点!国内隐私计算学者在 USENIX Security 2023 顶会上的成果
USENIX Security 是国际公认的网络安全与隐私计算领域的四大顶级学术会议之一、CCF(中国计算机学会) 推荐的 A 类会议。 每年的 USENIX Security 研讨会都会汇集大量研究人员、从业人员、系统管理员、系统程序员和其他对计算机系统、网络安全和隐私最新进展感兴趣的人。 近日,在 2023 年 USENIX 安全研讨会(USENIX Security Symposium-2023)上,共计发表了隐私计算相关文章 51 篇,涉及联邦学习、同态加密、安全多方计算等多个隐私计算领域,作为网络安全与隐私计算领域的顶级会议,它的录取率常年低于 20%。 今年的会议上国内学者贡献了非常多的优秀成果,我们选取几个热门领域来简单盘点一下。 一、成果领域分布 成果领域 入选篇数 国内研究者参与优秀成果 差分隐私(DP) 3 Fine-grained Poisoning Attack to Local Differential Privacy Protocols for Mean and Variance Estimation (第一作者:西安电子科技大学) 可信执行环境(TEE) ...
- 下一篇
Dante OSS 1.0.0 发布,一款简化 Minio 操作的开源框架
简介 | Intro Dante OSS 是一款简化Minio操作的开源框架。通过对原有 Minio Java SDK 的深度封装,简化 Minio API 使用复杂度,提升 Minio 使用的便捷性,降低 Minio 应用开发门槛。 MinIO 是一款高性能、分布式的对象存储系统。Minio这款开源的分布式对象存储服务在国外已经相当受欢迎,并且国内也有多中小型互联网公司使用它来作为对象存储服务。虽然 Minio 相关的资料和示例在网络上已经非常丰富,但是为什么还要推出 Dante OSS 这样的项目? 初次接触 Minio,特别是想要通过 Java 集成 Minio 开发对象存储应用是,还是需要投入一定的时间和精力去了解其原理阅读相关文档。 Minio SDK 中的函数方法,涉及的参数较多,抽象层度比较高,每次使用都需要反复查阅源代码才能摸清具体使用方式。 想要与已有的应用进行整合,多少都要投入时间精力,进行一定程度的封装和改造。 网上相关资料多,要么比较零散不成体系,要么比较单一仅针对常规上传下载应用,Minio 自身很多特性都不涉及。 发布 | Releases 【本次更新内容】...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Windows10,CentOS7,CentOS8安装Nodejs环境
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Docker使用Oracle官方镜像安装(12C,18C,19C)