Wow 2.5.0 发布,告别 ORM、贫血模型、阻抗失配


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

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

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

更新内容 🎉 🎉 🎉

支持标记忽略事件溯源接口(IgnoreSourcing)

  • 依赖: 更新 CosId 版本 v2.4.0
  • 依赖: 更新 io.gitlab.arturbosch.detekt 版本 v1.23.1
  • 特性: 增强 Spring-Boot 配置提示
  • 特性: 增强单元测试套件断言失败提示
  • 依赖: 更新 com.google.guava:guava 版本 v32.1.2-jre
  • 特性: 支持标记忽略事件溯源接口(IgnoreSourcing)
    • 应用场景:执行聚合根命令时,业务校验失败需要生成失败事件以便下游订阅者处理。并且不需要溯源领域事件。
    • 触发条件:
      • 领域事件标记 ErrorInfo ,标记该事件为失败事件
      • 领域事件标记 IgnoreSourcing
      • 领域事件版本=1
    • 影响:
      • 忽略该领域事件的溯源,且不会变更聚合版本。
      • 将忽略未初始化的状态聚合发送到状态事件总线。
      • 聚合快照处理器将无法接受到该状态事件,即不会存储到快照仓库。

架构图

Wow-Architecture

事件源

Wow-EventSourcing

可观测性

Wow-Observability

Spring WebFlux 集成

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

OpenAPI-Swagger

测试套件: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 .

Aggregate Unit Test (AggregateVerifier)

Aggregate Test

internal class OrderTest {

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.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()
}
}
return 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 createOrder() {
mockCreateOrder()
}

@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()
}

/**
 * 创建订单-下单价格与当前价格不一致
 */
@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.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.plus(BigDecimal.valueOf(1)) }.first().toMono()
}
}
aggregateVerifier<Order, OrderState>()
.inject(DefaultCreateOrderSpec(inventoryService, pricingService))
.given()
.`when`(CreateOrder(customerId, orderItems, SHIPPING_ADDRESS, false))
/*
 * 期望:价格不一致异常.
 */
.expectErrorType(PriceInconsistencyException::class.java).verify()
}
}

Saga Unit Test (SagaVerifier)

Saga Test

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
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-07-21

开源地址: github: https://github.com/sagframe/sagacity-sqltoy gitee: https://gitee.com/sagacity/sagacity-sqltoy idea 插件 (可直接在 idea 中检索安装):  https://github.com/threefish/sqltoy-idea-plugins sqltoy 脚手架项目:https://gitee.com/momoljw/ss

2022-08-20

原文由 Yury Selivanov(@1st1)和 Elvis Pranskevichus(@elprans)发布于 2022 年 7 月 28 日。 在 Hacker News 上查看 EdgeDB 2.0 的英文讨论,以及在 YouTube 上观看上线直播的回放。也可以在 Gitee Issues 上参与中文讨论,发布会视频也发在了 B 站

2023-07-05

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

2023-06-12

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

2023-09-12

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

2023-02-16

angover 的开发一直停滞不前;直至近日,André Zwing 才宣布发布了一些更新内容。 根据介绍,Hangover 在 64 位 Arm 和其他 CPU 架构上运行 Windows 应用程序/游戏应用方面非常有前途,但近几年受 WoW64 工作进展影响而被搁置。WoW64

2023-10-22

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

2023-06-09

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

2023-07-25

时该标志也出现在了推特的总部大楼上。 马斯克今日发布推文称,X.com 现已定向至 Twitter 原网址 twitter.com ,临时 X Logo 将于今日晚些时候启用。 根据马斯克转发的现任 Twttier CEO 推文,X 和他之前提到过的“超级 App”愿景

2023-04-28

a 的响应式流、响应式数据库 API 等。  Quarkus 3.0 现已发布,该版本基于 Jakarta EE 10,并包含了一些新功能和增强功能的实现。 新的 Dev UI Quarkus 3.0 现在有一个新的 Dev UI,它更具可扩展性和更易于使用,以及新的改进

2023-04-24

SQLAlchemy 2.0.10 现已发布。 SQLAlchemy 是一个 Python 的 SQL 工具包以及数据库对象映射 (ORM) 框架。它包含整套企业级持久化模式,专门用于高效和高性能的数据库访问。 上次发布的 2.0.9 是一个紧急版本,2.0.10 版重新

2022-10-15

SQLAlchemy 2.0.0 首个 Beta 版本已发布。SQLAlchemy 是一个 Python 的 SQL 工具包以及数据库对象映射 (ORM) 框架。它包含整套企业级持久化模式,专门用于高效和高性能的数据库访问。 发布公告写道,2.0 旨在适应现代化 Python 的实际使

2023-01-11

SQLAlchemy 2.0 发布了第 2 个 RC 版本。 下载地址:https://www.sqlalchemy.org/download.html SQLAlchemy 是一个 Python 的 SQL 工具包以及数据库对象映射 (ORM) 框架。它包含整套企业级持久化模式,专门用于高效和高性能的数据库访问。 发布公

2022-11-06

SQLAlchemy 2.0 的第三个 Beta 版本已发布。 SQLAlchemy 是一个 Python 的 SQL 工具包以及数据库对象映射 (ORM) 框架。它包含整套企业级持久化模式,专门用于高效和高性能的数据库访问。 发布公告写道,SQLAlchemy 2.0.0 第三个 Beta 版