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

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

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

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

更新内容 🎉 🎉 🎉

  • 特性(RouteSpec): 命令路由(@CommandRoute)路径参数支持未绑定命令体字段
  • 特性: 支持使用租户ID(TenantId)参数构造状态聚合
  • 依赖: 更新 org.testcontainers:testcontainers-bom 版本 v1.19.1
  • 依赖: 更新 io.opentelemetry:opentelemetry-bom 版本 v1.31.0
  • 依赖: 更新 io.mockk:mockk 版本 v1.13.8

架构图

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

优秀的个人博客,低调大师

微信关注我们

原文链接:https://www.oschina.net/news/261083/wow-2-6-8

转载内容版权归作者及来源网站所有!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

相关文章

发表评论

资源下载

更多资源
Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的、适应高吞吐量的数据库方案。

Apache Tomcat7、8、9(Java Web服务器)

Apache Tomcat7、8、9(Java Web服务器)

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。

Eclipse(集成开发环境)

Eclipse(集成开发环境)

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。

Sublime Text 一个代码编辑器

Sublime Text 一个代码编辑器

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。