告别 ORM、贫血模型、阻抗失配 | Wow 2.9.2 发布
基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架
领域驱动 | 事件驱动 | 测试驱动 | 声明式设计 | 响应式编程 | 命令查询职责分离 | 事件溯源
更新内容 🎉 🎉 🎉
适配
Spring-Boot 3.2.0
- 依赖: 更新
org.springframework.boot:spring-boot-dependencies
版本v3.2.0
- 依赖: 更新
me.ahoo.cosid:cosid-bom
版本v2.6.0
- 重构: 使用 CosId 的
HostAddressSupplier
替换InetUtils
,移除Spring-Cloud-Commons
依赖 - 案例: 新增解锁金额(
UnfreezeAccount
)命令 (经典 DDD 银行转账案例(JAVA))
架构图
性能测试 (Example)
- 测试代码:Example
- 测试场景:加入购物车、下单
- 命令发送等待模式(
WaitStrategy
):SENT
、PROCESSED
部署
测试报告
加入购物车
WaitStrategy
:SENT
WaitStrategy
:PROCESSED
下单
WaitStrategy
:SENT
WaitStrategy
:PROCESSED
事件源
可观测性
OpenAPI (Spring WebFlux 集成)
自动注册 命令 路由处理函数 (
HandlerFunction
) ,开发人员仅需编写领域模型,即可完成服务开发。
测试套件:80%+ 的测试覆盖率轻而易举
Given -> When -> Expect .
前置条件
- 理解 领域驱动设计:《实现领域驱动设计》、《领域驱动设计:软件核心复杂性应对之道》
- 理解 命令查询职责分离(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)
- EventStore
- 命令等待策略(
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
toRouterFunction
- 可观测性
- OpenTelemetry
- OpenAPI
-
WowMetadata
Generator-
wow-compiler
-
Example
单元测试套件
80%+ 的测试覆盖率轻而易举。
Given -> When -> Expect .
Aggregate Unit Test (AggregateVerifier
)
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
)
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 |
---|---|---|
加载聚合
聚合状态流
发送命令
命令与事件流
Saga - OrderProcessManager (Demo)
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
当OLAP碰撞Serverless,看ByteHouse如何建设下一代云计算架构
更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群 作为云计算的下一个迭代,Serverless 可以使开发者更专注于构建产品中的应用,而无需考虑底层堆栈问题。伴随着近年来相关技术成熟度的增加,市场对 Serverless 的接受程度也变得越来越高。可以说时至今日,Serverless 已迈入了向成熟稳定方向发展的高速轨道。 作为一款火山引擎推出的云原生数据仓库,ByteHouse 基于开源 ClickHouse 构建,并在字节跳动内外部场景的检验下,对 OLAP 引擎能力、性能、运维、架构进一步升级。除此之外,ByteHouse 也在 Serverless 方向探索,基于 cloud-native 云原生的理念构建了全新一代的数据仓库,架构上进行了三层解耦,期望在 Serverless 的加持下,提供更稳定、可靠、可信的分析服务,让开发人员时间精力从基础设施运维优化上解放,更聚焦在核心业务功能中。 然而,在 OLAP 领域的 Serverless 技术实现上,仍然存在一些技术难点。 首先,OLAP 数据分析涉及到存储、网络、操作系统、数据库、AI 等...
- 下一篇
通用、灵活、高性能的分布式 ID 生成器 | CosId 2.6.0 发布
CosId通用、灵活、高性能分布式 ID 生成器 更新内容 🎉 🎉 🎉 适配Spring-Boot 3.2.0 依赖: 更新org.springframework.boot:spring-boot-dependencies版本v3.2.0 依赖: 更新org.mybatis.spring.boot:mybatis-spring-boot-starter版本v3.0.3 简介 CosId旨在提供通用、灵活、高性能的分布式 ID 生成器。 SnowflakeId:单机 TPS 性能:409W/sJMH 基准测试, 主要解决时钟回拨问题、机器号分配问题并且提供更加友好、灵活的使用体验。 SegmentId: 每次获取一段 (Step) ID,来降低号段分发器的网络 IO 请求频次提升性能。 IdSegmentDistributor: 号段分发器(号段存储器) RedisIdSegmentDistributor: 基于Redis的号段分发器。 JdbcIdSegmentDistributor: 基于Jdbc的号段分发器,支持各种关系型数据库。 ZookeeperIdSegmentDis...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS8编译安装MySQL8.0.19
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Docker使用Oracle官方镜像安装(12C,18C,19C)