您现在的位置是:首页 > 文章详情

领域建模即微服务开发 | Wow 2.10.0 发布

日期:2023-12-18点击:99

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

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

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

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

🎉 更新内容 🎉

新增 事件补偿 模块,以支持事件自动补偿!

  • 重构: 事件补偿器(EventCompensator)API
  • 重构: CommandMessage / DomainEvent / ProcessorInfoData 移动到 api 模块
  • 依赖: 升级 com.google.devtools.ksp:symbol-processing-api v1.9.21-1.0.16
  • 特性: 新增聚合ID转换器(AggregateIdConverter) - OpenAPI
  • 特性: 新增事件补偿模块
  • 特性: 支持补偿过滤器(CompensationFilter),用于发送领域事件处理异常处理结果
  • 特性: 支持自动配置事件补偿客户端模块
  • 特性: 新增事件补偿模块 CI 测试
  • 特性: 支持事件补偿服务端 Docker 镜像发布
  • 特性: 支持补偿规范(CompensationSpec)配置
  • 重构: 将基于时间的分片替换为sipHash64分片,以防止相同聚合ID分布到不同分区导致合并失败 - ClickHouse (ETL)

快速开始

使用 Wow 项目模板快速创建基于 Wow 框架的 DDD 项目。

特性概览

Wow-Features

架构图

Wow-Architecture

性能测试 (Example)

  • 测试代码:Example
  • 测试场景:加入购物车、下单
  • 命令发送等待模式(WaitStrategy):SENTPROCESSED

部署

测试报告

加入购物车

WaitStrategy: SENT

SENT

WaitStrategy: PROCESSED

PROCESSED

下单

WaitStrategy: SENT

SENT

WaitStrategy: PROCESSED

PROCESSED

事件源

Wow-EventSourcing

可观测性

Wow-Observability

OpenAPI (Spring WebFlux 集成)

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

Wow-Spring-WebFlux-Integration

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

Given -> When -> Expect .

Wow-CI-Flow

前置条件

  • 理解 领域驱动设计:《实现领域驱动设计》、《领域驱动设计:软件核心复杂性应对之道》
  • 理解 命令查询职责分离(CQRS)
  • 理解 事件源架构
  • 理解 响应式编程

Example

订单服务(Kotlin)

Example-Order

银行转账(JAVA)

Example-Transfer

单元测试套件

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

Test Coverage

Given -> When -> Expect .

Aggregate Unit Test (AggregateVerifier)

Aggregate Test

 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)

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

原文链接:https://www.oschina.net/news/271489/wow-2-10-0-released
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章