# Wow
**Repository Path**: easter/Wow
## Basic Information
- **Project Name**: Wow
- **Description**: 让领域驱动设计变得触手可得 - 基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架
- **Primary Language**: Kotlin
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: https://github.com/Ahoo-Wang/Wow
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 90
- **Created**: 2023-07-19
- **Last Updated**: 2023-07-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Wow
> [中文文档](README.zh-CN.md)
A Modern Reactive CQRS Architecture Microservice development framework based on DDD and EventSourcing.
[](https://github.com/Ahoo-Wang/Wow/blob/mvp/LICENSE)
[](https://github.com/Ahoo-Wang/Wow/releases)
[](https://maven-badges.herokuapp.com/maven-central/me.ahoo.wow/wow-core)
[](https://app.codacy.com/gh/Ahoo-Wang/Wow/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[](https://codecov.io/gh/Ahoo-Wang/Wow)
[](https://github.com/Ahoo-Wang/Wow)
[](https://github.com/KotlinBy/awesome-kotlin)
**Domain-Driven** | **Event-Driven** | **Test-Driven** | **Declarative-Design** | **Reactive Programming** | **Command
Query Responsibility Segregation** | **Event Sourcing**
## Architecture
### Event Sourcing
### Observability
### OpenAPI (Spring WebFlux Integration)
> Automatically register the `Command` routing processing function (`HandlerFunction`), and developers only need to
> write the domain model to complete the service development.
### Test suite: 80%+ test coverage is very easy
> Given -> When -> Expect .
## Preconditions
- Understanding **Domain Driven Design**:《Implementing Domain-Driven Design》,《Domain-Driven Design: Tackling Complexity
in the Heart of Software》
- Understanding **Command Query Responsibility Segregation**(CQRS)
- Understanding **EventSourcing**
- Understanding **Reactive Programming**
## Features
- [x] Aggregate Modeling
- [x] Single Class
- [x] Inheritance Pattern
- [x] Aggregation Pattern
- [x] Saga Modeling
- [x] `StatelessSaga`
- [x] Test Suite
- [x] The Technology Compatibility Kit
- [x] `AggregateVerifier`
- [x] `SagaVerifier`
- [x] EventSourcing
- EventStore
- [x] MongoDB (Recommend)
- [x] R2dbc
- [x] Database Sharding
- [x] Table Sharding
- [x] Redis
- Snapshot
- [x] MongoDB
- [x] R2dbc
- [x] Database Sharding
- [x] Table Sharding
- [x] ElasticSearch
- [x] Redis (Recommend)
- [x] Command `WaitStrategy`
- [x] `SENT` : Send a completion signal after the command is sent
- [x] `PROCESSED` : Send a completion signal when command processing is complete
- [x] `SNAPSHOT` : Send a completion signal after the snapshot generation is complete
- [x] `PROJECTED` : Send a completion signal when the event generated by the command has been projected
- [x] CommandBus
- [x] `InMemoryCommandBus`
- [x] `KafkaCommandBus` (Recommend)
- [x] `RedisCommandBus`
- [x] `LocalFirstCommandBus`
- [x] DomainEventBus
- [x] `InMemoryDomainEventBus`
- [x] `KafkaDomainEventBus` (Recommend)
- [x] `RedisDomainEventBus`
- [x] `LocalFirstDomainEventBus`
- [x] StateEventBus
- [x] `InMemoryStateEventBus`
- [x] `KafkaStateEventBus` (Recommend)
- [x] `RedisStateEventBus`
- [x] `LocalFirstStateEventBus`
- [x] Spring Integration
- [x] Spring Boot Auto Configuration
- [x] Automatically register `CommandAggregate` to `RouterFunction`
- [x] Observability
- [x] OpenTelemetry
- [x] OpenAPI
- [x] `WowMetadata` Generator
- [x] `wow-compiler`
## Example
[Example](./example)
## Unit Test Suite
### 80%+ test coverage is very easy.

> Given -> When -> Expect .
### Aggregate Unit Test (`AggregateVerifier`)
[Aggregate Test](./example/example-domain/src/test/kotlin/me/ahoo/wow/example/domain/order/OrderTest.kt)
```kotlin
internal class OrderTest {
private fun mockCreateOrder(): VerifiedStage {
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 {
return orderItems.filter { it.productId == productId }.map { it.quantity }.first().toMono()
}
}
val pricingService = object : PricingService {
override fun getProductPrice(productId: String): Mono {
return orderItems.filter { it.productId == productId }.map { it.price }.first().toMono()
}
}
return aggregateVerifier(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()
.inject(mockk(), "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 {
return orderItems.filter { it.productId == productId }
/*
* 模拟库存不足
*/
.map { it.quantity - 1 }.first().toMono()
}
}
val pricingService = object : PricingService {
override fun getProductPrice(productId: String): Mono {
return orderItems.filter { it.productId == productId }.map { it.price }.first().toMono()
}
}
aggregateVerifier()
.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 {
return orderItems.filter { it.productId == productId }.map { it.quantity }.first().toMono()
}
}
val pricingService = object : PricingService {
override fun getProductPrice(productId: String): Mono {
return orderItems.filter { it.productId == productId }
/*
* 模拟下单价格、商品定价不一致
*/
.map { it.price.plus(BigDecimal.valueOf(1)) }.first().toMono()
}
}
aggregateVerifier()
.inject(DefaultCreateOrderSpec(inventoryService, pricingService))
.given()
.`when`(CreateOrder(customerId, orderItems, SHIPPING_ADDRESS, false))
/*
* 期望:价格不一致异常.
*/
.expectErrorType(PriceInconsistencyException::class.java).verify()
}
}
```
### Saga Unit Test (`SagaVerifier`)
[Saga Test](./example/example-domain/src/test/kotlin/me/ahoo/wow/example/domain/cart/CartSagaTest.kt)
```kotlin
class CartSagaTest {
@Test
fun onOrderCreated() {
val orderItem = OrderItem(
GlobalIdGenerator.generateAsString(),
GlobalIdGenerator.generateAsString(),
BigDecimal.valueOf(10),
10,
)
sagaVerifier()
.`when`(
mockk {
every {
customerId
} returns "customerId"
every {
items
} returns listOf(orderItem)
every {
fromCart
} returns true
},
)
.expectCommandBody {
assertThat(it.id, equalTo("customerId"))
assertThat(it.productIds, hasSize(1))
assertThat(it.productIds.first(), equalTo(orderItem.productId))
}
.verify()
}
}
```
## Design
### Modeling
| **Single Class** | **Inheritance Pattern** | **Aggregation Pattern** |
|----------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------|
|  |  |  |
### Load Aggregate
### Aggregate State Flow
### Send Command
### Command And Event Flow
### Saga - OrderProcessManager (Demo)