代码拉取完成,页面将自动刷新
同步操作将从 Ahoo-Wang/Wow 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架。
领域驱动 | 事件驱动 | 测试驱动 | 声明式设计 | 响应式编程 | 命令查询职责分离 | 事件溯源
WaitStrategy
):SENT
、PROCESSED
WaitStrategy
:SENT
WaitStrategy
:PROCESSED
WaitStrategy
:SENT
WaitStrategy
:PROCESSED
自动注册 命令 路由处理函数(
HandlerFunction
) ,开发人员仅需编写领域模型,即可完成服务开发。
Given -> When -> Expect .
使用 Wow 框架模板快速创建项目。 模板地址:Wow-Project-Template
StatelessSaga
AggregateVerifier
SagaVerifier
WaitStrategy
)
SENT
: 命令发送成功后发送完成信号PROCESSED
: 命令处理完成后发送完成信号SNAPSHOT
: 快照生成完成后发送完成信号PROJECTED
: 命令产生的事件被投影后发送完成信号InMemoryCommandBus
KafkaCommandBus
(Recommend)RedisCommandBus
LocalFirstCommandBus
InMemoryDomainEventBus
KafkaDomainEventBus
(Recommend)RedisDomainEventBus
LocalFirstDomainEventBus
InMemoryStateEventBus
KafkaStateEventBus
(Recommend)RedisStateEventBus
LocalFirstStateEventBus
CommandAggregate
to RouterFunction
WowMetadata
Generator
wow-compiler
Given -> When -> Expect .
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()
}
}
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 |
---|---|---|
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。