Skip to content
/ Wow Public

Modern Reactive CQRS Architecture Microservice development framework based on DDD and EventSourcing | cơ vu DDD & EventSourcing đích hiện đại hưởng ứng thức CQRS giá cấu vi phục vụ khai phát khuông giá

License

Notifications You must be signed in to change notification settings

Ahoo-Wang/Wow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Repository files navigation

Wow:A Modern Reactive CQRS Architecture Microservice development framework based on DDD and EventSourcing

Wow: Modern Reactive CQRS Architecture Microservice development framework based on DDD and EventSourcing

Trung văn văn đương

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

Domain-Driven|Event-Driven|Test-Driven|Declarative-Design|Reactive Programming|Command Query Responsibility Segregation|Event Sourcing

Quick Start

UseWow Project Templateto quickly create a DDD project based on the Wow framework.

Features Overview

Wow-Features

Architecture

Wow-Architecture

Performance Test (Example)

  • Test Code:Example
  • Test Case: Add To Shopping Cart / Create Order
  • CommandWaitStrategy:SENT,PROCESSED

Deployment

Test Report

Add To Shopping Cart

WaitStrategy:SENTMode, TheAddCartItemcommand write request API After 2 minutes of stress testing, the average TPS was59625,the peak was82312,and the average response time was29ms.

AddCartItem-SENT

WaitStrategy:PROCESSEDMode, TheAddCartItemcommand write request API After 2 minutes of stress testing, the average TPS was18696,the peak was24141,and the average response time was239ms.

AddCartItem-PROCESSED

Create Order

WaitStrategy:SENTMode, TheCreateOrdercommand write request API After 2 minutes of stress testing, the average TPS was47838,the peak was86200,and the average response time was217ms.

CreateOrder-SENT

WaitStrategy:PROCESSEDMode, TheCreateOrdercommand write request API After 2 minutes of stress testing, the average TPS was18230,the peak was25506,and the average response time was268ms.

CreateOrder-PROCESSED

Event Sourcing

Wow-EventSourcing

Observability

Wow-Observability

OpenAPI (Spring WebFlux Integration)

Automatically register theCommandrouting processing function (HandlerFunction), and developers only need to write the domain model to complete the service development.

Wow-Spring-WebFlux-Integration

Test suite: 80%+ test coverage is very easy

Given -> When -> Expect.

Wow-CI-Flow

Preconditions

  • UnderstandingDomain Driven Design:《Implementing Domain-Driven Design》,《Domain-Driven Design: Tackling Complexity in the Heart of Software》
  • UnderstandingCommand Query Responsibility Segregation(CQRS)
  • UnderstandingEventSourcing
  • UnderstandingReactive Programming

Order Service ( Kotlin )

Example-Order

Transfer ( JAVA )

Example-Transfer

Unit Test Suite

80%+ test coverage is very easy.

Test Coverage

Given -> When -> Expect.

Aggregate Unit Test (AggregateVerifier)

Aggregate Test

internalclassOrderTest{

@Test
privatefuncreateOrder() {
valtenantId=GlobalIdGenerator.generateAsString()
valcustomerId=GlobalIdGenerator.generateAsString()

valorderItem=OrderItem(
GlobalIdGenerator.generateAsString(),
GlobalIdGenerator.generateAsString(),
BigDecimal.valueOf(10),
10,
)
valorderItems=listOf(orderItem)
valinventoryService=object:InventoryService{
overridefungetInventory(productId:String):Mono<Int> {
returnorderItems.filter { it.productId==productId }.map { it.quantity }.first().toMono()
}
}
valpricingService=object:PricingService{
overridefungetProductPrice(productId:String):Mono<BigDecimal> {
returnorderItems.filter { it.productId==productId }.map { it.price }.first().toMono()
}
}
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
funcreateOrderGivenEmptyItems() {
valcustomerId=GlobalIdGenerator.generateAsString()
aggregateVerifier<Order,OrderState>()
.inject(mockk<CreateOrderSpec>(),"createOrderSpec")
.given()
.`when`(CreateOrder(customerId,listOf(),SHIPPING_ADDRESS,false))
.expectErrorType(IllegalArgumentException::class.java)
.expectStateAggregate {
/*
* cai tụ hợp đối tượng xử vu vị sơ thủy hóa trạng thái, tức cai tụ hợp vị sang kiến thành công.
*/
assertThat(it.initialized, equalTo(false))
}.verify()
}

/**
* sang kiến đính đan - khố tồn bất túc
*/
@Test
funcreateOrderWhenInventoryShortage() {
valcustomerId=GlobalIdGenerator.generateAsString()
valorderItem=OrderItem(
GlobalIdGenerator.generateAsString(),
GlobalIdGenerator.generateAsString(),
BigDecimal.valueOf(10),
10,
)
valorderItems=listOf(orderItem)
valinventoryService=object:InventoryService{
overridefungetInventory(productId:String):Mono<Int> {
returnorderItems.filter { it.productId==productId }
/*
* mô nghĩ khố tồn bất túc
*/
.map { it.quantity-1}.first().toMono()
}
}
valpricingService=object:PricingService{
overridefungetProductPrice(productId:String):Mono<BigDecimal> {
returnorderItems.filter { it.productId==productId }.map { it.price }.first().toMono()
}
}

aggregateVerifier<Order,OrderState>()
.inject(DefaultCreateOrderSpec(inventoryService, pricingService))
.given()
.`when`(CreateOrder(customerId, orderItems,SHIPPING_ADDRESS,false))
/*
* kỳ vọng: Khố tồn bất túc dị thường.
*/
.expectErrorType(InventoryShortageException::class.java)
.expectStateAggregate {
/*
* cai tụ hợp đối tượng xử vu vị sơ thủy hóa trạng thái, tức cai tụ hợp vị sang kiến thành công.
*/
assertThat(it.initialized, equalTo(false))
}.verify()
}
}

Saga Unit Test (SagaVerifier)

Saga Test

classCartSagaTest{

@Test
funonOrderCreated() {
valorderItem=OrderItem(
GlobalIdGenerator.generateAsString(),
GlobalIdGenerator.generateAsString(),
BigDecimal.valueOf(10),
10,
)
sagaVerifier<CartSaga>()
.`when`(
mockk<OrderCreated> {
every {
customerId
} returns"customerId"
every {
items
} returnslistOf(orderItem)
every {
fromCart
} returnstrue
},
)
.expectCommandBody<RemoveCartItem> {
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
Single Class - Modeling Inheritance Pattern- Modeling Aggregation Pattern- Modeling

Load Aggregate

Load Aggregate

Aggregate State Flow

Aggregate State Flow

Send Command

Send Command

Command And Event Flow

Command And Event Flow

Event Compensation

Use Case

Event-Compensation-UserCase

Execution Sequence Diagram

Event-Compensation

Dashboard

Compensation-Dashboard

Compensation-Dashboard

Compensation-Dashboard

Compensation-Dashboard-Error