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

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

时间:2023-11-12 10:11:07浏览次数:38  
标签:val 事件驱动 驱动 generateAsString 溯源 GlobalIdGenerator customerId orderItems productI

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

 

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

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

架构图

Wow-Architecture

事件源

Wow-EventSourcing

可观测性

Wow-Observability

OpenAPI (Spring WebFlux 集成)

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

Wow-Spring-WebFlux-Integration

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

Given -> When -> Expect .

Wow-CI-Flow

前置条件

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

特性

    • EventStore
    • Snapshot

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 ClassInheritance PatternAggregation 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

作者:Ahoo Wang (阿虎)

Github: https://github.com/Ahoo-Wang/

SmartSql(高性能、高生产力,超轻量级的ORM!): https://github.com/Ahoo-Wang/SmartSql

SmartCode(不只是代码生成器!): https://github.com/Ahoo-Wang/SmartCode

CoSky 高性能、低成本微服务治理平台 : https://github.com/Ahoo-Wang/CoSky

CosId 通用、灵活、高性能的分布式 ID 生成器 : https://github.com/Ahoo-Wang/CosId

Wow 基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架: https://github.com/Ahoo-Wang/Wow

CoSec 基于 RBAC 和策略的多租户响应式安全框架: https://github.com/Ahoo-Wang/CoSec


本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

标签:val,事件驱动,驱动,generateAsString,溯源,GlobalIdGenerator,customerId,orderItems,productI
From: https://www.cnblogs.com/Leo_wl/p/17826795.html

相关文章

  • 一个操作系统的设计与实现——第9章 硬盘驱动
    操作系统应当具备读写硬盘的能力。因此,本章将要实现的是硬盘驱动。硬盘驱动由两个函数构成:读硬盘函数与写硬盘函数。9.1读硬盘想要读硬盘,就需要提供以下三个信息:起始扇区号读取的扇区数数据存储的地址需要注意的是:读取的扇区数只能是一个8字节的整数。由于读硬盘需要使......
  • 一个操作系统的设计与实现——第6章 显卡驱动
    进入内核以后,应该做些什么呢?本章将实现一个最容易看到效果的模块:显卡驱动。6.1什么是驱动驱动这个词听起来很高大上,但实际上很简单,就是硬件的接口函数。在软件工程中,可以使用接口封装和简化设计,硬件也是一样。例如:想要读硬盘,需要很多指令设定好几个端口,然后等待硬盘就绪,最后才......
  • PCF8574芯片介绍及驱动方法
    (文章目录)前言本篇文章带大家学习PCF8574芯片,了解PCF8574芯片有什么作用,以及学习PCF8574的控制方法。一、PCF8574芯片介绍PCF8574是TI(TexasInstruments)公司生产的一种常见的I/O扩展芯片,用于将微控制器的少量GPIO引脚扩展为更多的GPIO接口。它采用I2C总线(串行通信协议)进行与......
  • STM32驱动OLED实现充电动画
    先看效果因为之前在项目中OLED一般只显示字符,今天闲来无事了解了一下取模功能,话不多说,直接开始教学。首先寻找一张你想显示的图片使用画图打开(重新调整大小,128乘以64)接着保存,注意是以单色图保存,下面是保存为单色的样子接着取模(使用的软件为Pctolcd2002),模式选择图片模式,按照下图修......
  • 开发指南,自研关键字驱动框架
    开发指南环境准备安装Python,3.8以上版本安装poetry包管理工具,pipinstallpoetry克隆代码,gitclonehttps://github.com/dongfanger/tep准备就绪,撸起袖子干!目录结构distpoetrybuild生成目标文件,用于发布pypitep核心代码tests测试代码utils工具包......
  • Linux MIPI 摄像头驱动框架编写(RN6752解码芯片)
    一、概述在编写MIPI摄像头驱动之前,需要先了解Media子系统的链路关系,这里我就不介绍了,需要的看我之前的笔记:LinuxMedia子系统链路分析。理解Media子系统链路关系后,会发现ISP不论是在摄像头端,还是集成在SOC中,驱动程序都是差不多的。多观察一下开发板中的其他案例,便会......
  • 大型集团企业数据治理方案,以“应用驱动”的数据治理策略 | 行业方案
    数据治理是推动大型集团企业转型升级、提升竞争优势、实现高质量发展的重要引擎。通过搭建大数据平台,实现对业务系统数据的采集、清理、建模、整合,建立一个符合业务需求的数据决策平台,形成企业数字化转型关键能力,支撑数据赋能业务价值,最终推动组织及管理升级,实现数字化转型。以某......
  • SATA硬件驱动器接口的可制造性问题详解
    SATA接口是硬盘与主机系统间的连接部件,作用是在硬盘缓存和主机内存之间传输数据。不同的硬盘接口,决定着硬盘与计算机之间的连接速度,在整个系统中,硬盘接口的优劣,直接影响着程序运行快慢和系统性能好坏。SATA接口介绍SATA(SerialATA)是串行ATA的缩写,是一种完全不同于并行ATA的新型......
  • Esxi 6.7下面的网卡驱动问题
    前话:我们在运维VMware ESXI主机时,经常会遇到紫屏,网络中断,存储访问慢等种疑难杂症。大多时间都是感觉无从下手,只能截图,重启,收集日记,上报厂家CASE。厂家大多的答复是固件,驱动的兼容问题。今天就分享一下如何使用ESXICLI 命令查询三个重灾区:网卡,HBA,RIAD卡信息。esxcli networknic......
  • 新方向!文心一言X具身智能,用LLM大模型驱动智能小车
    具身智能已成为近年来研究的热点领域之一。具身智能强调将智能体与实体环境相结合,通过智能体与环境的交互,来感知和理解世界,最终实现在真实环境中的自主决策和运动控制。如何基于文心大模型,低成本入门“具身智能”,并用身边的普通硬件就能快速搭建“能理解、会决策”的实物智能体呢?为......