# Wow
**Repository Path**: bistulpf/Wow
## Basic Information
- **Project Name**: Wow
- **Description**: 领域模型即服务 - 基于 DDD & EventSourcing 的现代响应式 CQRS 架构微服务开发框架
- **Primary Language**: Kotlin
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: https://wow.ahoo.me/
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 89
- **Created**: 2025-07-04
- **Last Updated**: 2025-07-04
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Wow : Modern Reactive CQRS Architecture Microservice development framework based on DDD and EventSourcing
> [中文文档](https://wow.ahoo.me/)
[](https://github.com/Ahoo-Wang/Wow/blob/main/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)
[](https://deepwiki.com/Ahoo-Wang/Wow)
**Domain-Driven** | **Event-Driven** | **Test-Driven** | **Declarative-Design** | **Reactive Programming** | **Command Query Responsibility Segregation** | **Event Sourcing**
## Quick Start
Use [Wow Project Template](https://github.com/Ahoo-Wang/wow-project-template) to quickly create a DDD project based on the Wow framework.
## Features Overview
## Architecture
## Performance Test (Example)
- Test Code: [Example](./example)
- Test Case: Add To Shopping Cart / Create Order
- Command `WaitStrategy`: `SENT`、`PROCESSED`
### Deployment
- [Redis](deploy/example/perf/redis.yaml)
- [MongoDB](deploy/example/perf/mongo.yaml)
- [Kafka](deploy/example/perf/kafka.yaml)
- [Application-Config](deploy/example/perf/config/mongo_kafka_redis.yaml)
- [Application-Deployment](deploy/example/perf/deployment.yaml)
### Test Report
#### Add To Shopping Cart
- [Request](deploy/example/request/AddCartItem.http)
- [Detailed Report(PDF)-SENT](./document/example/perf/Example.Cart.Add@SENT.pdf)
- [Detailed Report(PDF)-PROCESSED](./document/example/perf/Example.Cart.Add@PROCESSED.pdf)
> `WaitStrategy`:`SENT` Mode, The `AddCartItem` command write request API After 2 minutes of stress testing, the average TPS was *59625*, the peak was *82312*, and the average response time was *29* ms.
> `WaitStrategy`:`PROCESSED` Mode, The `AddCartItem` command write request API After 2 minutes of stress testing, the average TPS was *18696*, the peak was *24141*, and the average response time was *239* ms.
#### Create Order
- [Request](deploy/example/request/CreateOrder.http)
- [Detailed Report(PDF)-SENT](./document/example/perf/Example.Order.Create@SENT.pdf)
- [Detailed Report(PDF)-PROCESSED](./document/example/perf/Example.Order.Create@PROCESSED.pdf)
> `WaitStrategy`:`SENT` Mode, The `CreateOrder` command write request API After 2 minutes of stress testing, the average TPS was *47838*, the peak was *86200*, and the average response time was *217* ms.
> `WaitStrategy`:`PROCESSED` Mode, The `CreateOrder` command write request API After 2 minutes of stress testing, the average TPS was *18230*, the peak was *25506*, and the average response time was *268* ms.
## 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**
### Order Service(Kotlin)
[Example-Order](./example)
### Transfer(JAVA)
[Example-Transfer](./example/transfer)
## 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
class CartTest {
@Test
fun addCartItem() {
val ownerId = generateGlobalId()
val addCartItem = AddCartItem(
productId = "productId",
quantity = 1,
)
aggregateVerifier(ownerId)
.givenOwnerId(ownerId)
.whenCommand(addCartItem)
.expectNoError()
.expectEventType(CartItemAdded::class)
.expectState {
it.items.assert().hasSize(1)
}.expectStateAggregate {
it.ownerId.assert().isEqualTo(ownerId)
}
.verify()
}
@Test
fun givenStateWhenAdd() {
val addCartItem = AddCartItem(
productId = "productId",
quantity = 1,
)
aggregateVerifier()
.givenState(CartState(generateGlobalId()), 1)
.whenCommand(addCartItem)
.expectNoError()
.expectEventType(CartItemAdded::class)
.expectState {
it.items.assert().hasSize(1)
}
.verify()
}
@Test
fun addCartItemIfSameProduct() {
val addCartItem = AddCartItem(
productId = "productId",
quantity = 1,
)
aggregateVerifier()
.given(
CartItemAdded(
added = CartItem(
productId = addCartItem.productId,
quantity = 1,
),
),
)
.whenCommand(addCartItem)
.expectNoError()
.expectEventType(CartQuantityChanged::class)
.expectState {
it.items.assert().hasSize(1)
it.items.first().quantity.assert().isEqualTo(2)
}
.verify()
}
@Test
fun addCartItemIfUnCreated() {
val addCartItem = AddCartItem(
productId = "productId",
quantity = 1,
)
aggregateVerifier()
.given()
.whenCommand(addCartItem)
.expectNoError()
.expectEventType(CartItemAdded::class)
.expectState {
it.items.assert().hasSize(1)
}
.expectStateAggregate {
it.version.assert().isEqualTo(1)
}
.verify()
}
@Test
fun addCartItemGivenMax() {
val events = buildList {
for (i in 0..99) {
add(
CartItemAdded(
added = CartItem(
productId = "productId$i",
quantity = 1,
),
),
)
}
}.toTypedArray()
val addCartItem = AddCartItem(
productId = "productId",
quantity = 1,
)
aggregateVerifier()
.given(*events)
.whenCommand(addCartItem)
.expectErrorType(IllegalArgumentException::class)
.expectState {
it.items.assert().hasSize(MAX_CART_ITEM_SIZE)
}
.verify()
}
@Test
fun removeCartItem() {
val removeCartItem = RemoveCartItem(
productIds = setOf("productId"),
)
val added = CartItem(
productId = "productId",
quantity = 1,
)
aggregateVerifier()
.given(
CartItemAdded(
added = added,
),
)
.whenCommand(removeCartItem)
.expectEventType(CartItemRemoved::class)
.expectState {
it.items.assert().isEmpty()
}
.verify()
}
@Test
fun changeQuantity() {
val changeQuantity = ChangeQuantity(
productId = "productId",
quantity = 2,
)
val added = CartItem(
productId = "productId",
quantity = 1,
)
aggregateVerifier()
.given(
CartItemAdded(
added = added,
),
)
.whenCommand(changeQuantity)
.expectEventType(CartQuantityChanged::class)
.expectState {
it.items.assert().hasSize(1)
it.items.first().quantity.assert().isEqualTo(changeQuantity.quantity)
}
.verify()
}
@Test
fun onCreateThenDeleteThenRecover() {
val addCartItem = AddCartItem(
productId = "productId",
quantity = 1,
)
aggregateVerifier()
.whenCommand(addCartItem)
.expectNoError()
.expectEventType(CartItemAdded::class)
.expectState {
it.items.assert().hasSize(1)
}
.verify()
.then()
.whenCommand(DefaultDeleteAggregate)
.expectEventType(DefaultAggregateDeleted::class)
.expectStateAggregate {
it.deleted.assert().isTrue()
}.verify()
.then()
.whenCommand(DefaultDeleteAggregate::class)
.expectErrorType(IllegalAccessDeletedAggregateException::class)
.verify()
.then()
.whenCommand(DefaultRecoverAggregate)
.expectStateAggregate {
it.deleted.assert().isFalse()
}.verify()
.then()
.whenCommand(DefaultRecoverAggregate)
.expectErrorType(IllegalStateException::class)
.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 ownerId = generateGlobalId()
val orderItem = OrderItem(
id = generateGlobalId(),
productId = generateGlobalId(),
price = BigDecimal.valueOf(10),
quantity = 10,
)
sagaVerifier()
.whenEvent(
event = mockk {
every {
items
} returns listOf(orderItem)
every {
fromCart
} returns true
},
ownerId = ownerId
)
.expectCommand {
it.aggregateId.id.assert().isEqualTo(ownerId)
it.body.productIds.assert().hasSize(1)
it.body.productIds.assert().first().isEqualTo(orderItem.productId)
}
.verify()
}
}
```
## Design
### Modeling
| **Single Class** | **Inheritance Pattern** | **Aggregation Pattern** |
|----------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------|
|  |  |  |
### Load Aggregate
### Aggregate State Flow
### Send Command
### Command And Event Flow
## Event Compensation
### Use Case
### Execution Sequence Diagram
### Dashboard