Wow 2.1.2 发布,让领域驱动设计变得触手可得


基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架

License GitHub release Maven Central Codacy Badge codecov Integration Test Status Awesome Kotlin Badge

领域驱动 | 事件驱动 | 测试驱动 | 声明式设计  响应式编程  命令查询职责分离  事件源

更新内容 🎉 🎉 🎉

提升无租户模式易用性

  • 特性: Wow 编译器支持生成静态租户ID元数据
  • 特性: 扩展函数 asAggregateId 支持静态租户ID检测
  • 特性: 命令元数据 CommandMetadata 支持静态租户ID检测
  • 特性: Wow应用元数据 WowMetadata 支持静态租户ID检测

架构图

Wow-Architecture

事件源

Wow-EventSourcing

可观测性

Wow-Observability

Spring WebFlux 集成

自动注册 命令 路由处理函数 (HandlerFunction) ,开发人员仅需编写领域模型,即可完成服务开发。

Wow-Spring-WebFlux-Integration

测试套件:80%+ 的测试覆盖率轻而易举

Given -> When -> Expect .

Wow-CI-Flow

前置条件

  • 理解 领域驱动设计:《实现领域驱动设计》、《领域驱动设计:软件核心复杂性应对之道》
  • 理解 命令查询职责分离(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)
  •  命令等待策略(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 to RouterFunction
  •  可观测性
    •  OpenTelemetry
  •  OpenApi
  •  WowMetadata Generator
    •  wow-compiler

Example

Example

测试套件

80%+ 的测试覆盖率轻而易举。

Test Coverage

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
Single Class - Modeling Inheritance Pattern- Modeling Aggregation Pattern- Modeling

加载聚合

Load Aggregate

聚合状态流

Aggregate State Flow

发送命令

Send Command

命令与事件流

Command And Event Flow

Saga - OrderProcessManager (Demo)

OrderProcessManager

 


相關推薦

2023-09-12

多元共进|2023 Google 开发者大会精彩演讲回顾 基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架        领域驱动  |  事件驱动  |  测试驱动  |  声明

2023-06-09

基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架 领域驱动 | 事件驱动 | 测试驱动 | 声明式设计 | 响应式编程 | 命令查询职责分离 | 事件源 更新内容 🎉 🎉 🎉 消

2023-06-12

基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架 领域驱动 | 事件驱动 | 测试驱动 | 声明式设计 | 响应式编程 | 命令查询职责分离 | 事件源 更新内容 🎉 🎉 🎉 支

2023-08-06

基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架        领域驱动  |  事件驱动  |  测试驱动  |  声明式设计 | 响应式编程 | 命令查

2023-10-22

【源创会预告】1024 程序员节(的前两天),相约开源PHP办公室,我们一起聊 AI!>>> 基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架       领域驱动  |  事件驱动  |&nb

2022-09-16

能将助力高效构建优质应用,Jetpack Compose 1.2 稳定版也已发布,帮助开发者在创新中更得心应手。 Flutter 产品经理樊舟颖也提到新发布的 Flutter 3.3 专注于完善和性能改进,还增加了一系列新功能以优化开发体验。

2022-10-09

IJPay 让支付触手可及,封装了微信支付、QQ 支付、支付宝支付、银联支付、京东支付、PayPal 支付等常用的支付方式以及各种常用的接口。不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入

2023-02-09

钥方式与公钥证书方式   以后版本更新迭代会同时发布多个版本,标准版本使用JDK 1.8,其他版本会在标准版本号后面加JDK版本号,本次更新主要是支持 SpringBoot3, 并提供 SpringBoot3版本示例。      欢迎大家贡献

2023-02-01

IJPay 让支付触手可及,封装了微信支付、QQ 支付、支付宝支付、银联支付、京东支付、PayPal 支付等常用的支付方式以及各种常用的接口。不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入

2023-10-18

【源创会预告】1024 程序员节(的前两天),相约开源PHP办公室,我们一起聊 AI!>>> 冰盾简介 冰盾·主动防御系统是由前腾讯、前360的安全专家组成的冰镜安全团队倾力研发,为专业人士打造的免费的终端、主机主动防御系统

2023-07-08

的大会主题演讲中,华为常务董事、华为云CEO张平安重磅发布盘古大模型3.0和昇腾AI云服务。其中,盘古大模型3.0围绕“行业重塑”“技术扎根”“开放同飞”三大创新方向,持续打造核心竞争力,为行业客户、伙伴及开发者提

2024-01-19

Wine 9.0 稳定版已正式发布。 Wine (“Wine Is Not an Emulator” 的首字母缩写)是一个能够在多种 POSIX-compliant 操作系统(诸如 Linux,macOS 及 BSD 等)上运行 Windows 应用的兼容层。它不是像虚拟机或者模拟器一样模仿内部的 Windows 逻

2024-04-27

始终贴近并满足企业的实际业务需求。 三、开源共享,驱动电子签章普及 “开放签”不仅是一款产品,更是一份推动电子签章技术普及与普惠的行业倡议。我们坚信,唯有开放才能激发创新,唯有共享才能实现共赢。因此,

2023-06-29

为何从 2021 年以后不再发新版本,工作的重点转移到质量发布上,不再按季度发布节奏。由于目标是进行实质性的大型功能更新,所以目前仍不清楚下一个版本会在何时发布。 ReactOS 的 x64 移植目前仍处于起步阶段,但在稳定性