Repository navigation
feat(example): connect the order and inventory contexts of ddk-mall through integration events - #77
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
Third step of the
ddk-mallreference application. After steps 1 and 2 the order and inventory contexts existed side by side: an order did not reserve anything. This step connects them, and it is the first time DDK's reliable events run in a business flow with two sides: a publisher, a consumer that answers, and the publisher reacting to the answer.Changes
PENDING_STOCK. The order context publishesorder.placedandorder.cancelled; the inventory context reserves or releases stock and answers withinventory.stock-reservedorinventory.stock-reservation-rejected; the order then moves toPENDING_PAYMENTorCANCELLED.application.integrationpackage as records marked@IntegrationEvent. A handler translates domain events into them inside the business transaction, so DDK records them in the event publication registry together with the business data.adapter.messaging. Each context declares its own payload record with only the fields it uses and imports nothing from the publisher.platform.messaging: a dispatcher and two message sources. Withmall.messaging.transport=rocketmq(thecomposeprofile) messages come from RocketMQ, consumed in order per queue. Withlocal(the default) they are forwarded in-process on a single thread, through the same JSON decoding and the same handlers.docker-compose.ymlgains RocketMQ.sequenceDiagram participant O as Order context participant MQ as RocketMQ participant I as Inventory context O->>O: save order (PENDING_STOCK) + order.placed, one transaction O-->>MQ: delivered after commit MQ-->>I: order.placed I->>I: lock SKUs → transaction → inbox + reserve + outcome message I-->>MQ: stock-reserved / stock-reservation-rejected MQ-->>O: outcome O->>O: PENDING_PAYMENT / CANCELLEDWhere the inbox goes, in
InventoryService.reserveForOrder:Design decisions:
placedis handled beforecancelledfor the same order.MallFlowIntegrationTestruns the flow on MySQL, Redis and RocketMQ containers: place and cancel, an order beyond the stock, and a redeliveredorder.placedthat reserves once and answers once.背景
ddk-mall参考应用的第三步。前两步做完后,订单和库存两个上下文只是并排放着:下单并不会预占任何库存。这一步把它们连起来,也是 DDK 的可靠事件第一次跑在一个有来有往的业务流程里:发布方发出消息,消费方处理后回一个结果,发布方再根据结果推进。改动
PENDING_STOCK。订单上下文发出order.placed与order.cancelled;库存上下文据此预占或释放库存,并回inventory.stock-reserved或inventory.stock-reservation-rejected;订单随后进入PENDING_PAYMENT或CANCELLED。application.integration包里,是标了@IntegrationEvent的 record。一个处理器在业务事务里把领域事件翻译成契约,DDK 因此把它和业务数据一起登记进事件发布记录。adapter.messaging。每个上下文自己定义消息体,只声明用得到的字段,不引用发布方的任何类型。platform.messaging:一个分发器和两种消息来源。mall.messaging.transport=rocketmq(composeprofile)时消息来自 RocketMQ,按队列顺序消费;local(默认)时在进程内用单线程转发,走同样的 JSON 解析和同样的处理器。docker-compose.yml增加 RocketMQ。整体流程见上文时序图,去重登记的位置见上文伪代码。设计取舍:
placed一定先于cancelled被处理。MallFlowIntegrationTest在 MySQL、Redis、RocketMQ 容器上跑完整流程:下单再取消、超出库存的订单、以及一条被重复投递的order.placed只预占一次、只回复一次。