假如你是訂單服務(wù)的一名研發(fā),正在開發(fā)支付成功這個(gè)業(yè)務(wù)功能,在深度學(xué)習(xí) DDD 后,你寫出了一組漂亮的代碼。
@Transactionalpublic void paySuccess(Long orderId){ // 1. 獲取并驗(yàn)證訂單聚合根有效性 Order order = this.orderRepository.getById(orderId); if (order == null){ throw new IllegalArgumentException("訂單不存在"); } // 2. 修改價(jià)格 order.paySuccess(); // 3. 保存 Order 聚合 this.orderRepository.save(order); // 4. 通知物流服務(wù)進(jìn)行發(fā)貨}
成功上線后,系統(tǒng)運(yùn)行穩(wěn)定。隨后,你又陸續(xù)接到更多需求,比如:
更多的需求還在路上,此時(shí)原本漂亮的代碼已經(jīng)逐漸失控,變得有些面目全非:
@Transactionalpublic void paySuccess(Long orderId){ // 1. 獲取并驗(yàn)證訂單聚合根有效性 Order order = this.orderRepository.getById(orderId); if (order == null){ throw new IllegalArgumentException("訂單不存在"); } // 2. 修改價(jià)格 order.paySuccess(); // 3. 保存 Order 聚合 this.orderRepository.save(order); // 4. 通知物流服務(wù)進(jìn)行發(fā)貨 // 5. 為用戶發(fā)生觸達(dá)短信 // 發(fā)送觸達(dá)短信邏輯 // 6. 清理購(gòu)物車 // 7. 使用優(yōu)惠券,更新優(yōu)惠券狀態(tài) // 8. 提交風(fēng)控管理 // 其他代碼}
一些問(wèn)題慢慢的浮現(xiàn)出來(lái):
前期這些問(wèn)題你可能并不在意,直到有一天出現(xiàn)線上問(wèn)題:
聰明的你為了避免別人的服務(wù)到影響自己,悄悄的在每個(gè)業(yè)務(wù)調(diào)用時(shí)增加了 try-catch,但腐化仍舊在延續(xù)……
如果你也意識(shí)到這個(gè)問(wèn)題,那正是引入領(lǐng)域事件的好時(shí)機(jī)。
領(lǐng)域事件是領(lǐng)域模型的重要組成部分,用于表示在領(lǐng)域中發(fā)生的一些重要的業(yè)務(wù)事情或者狀態(tài)變化,它用來(lái)捕獲領(lǐng)域中的一些變更,記錄事件發(fā)生時(shí)的業(yè)務(wù)狀態(tài),并將這些數(shù)據(jù)傳輸?shù)接嗛喎剑蚤_展后續(xù)業(yè)務(wù)操作。
領(lǐng)域事件有以下一些特點(diǎn):
領(lǐng)域事件分為內(nèi)部領(lǐng)域事件和外部領(lǐng)域事件,想搞清楚兩者的區(qū)別,需要先回顧下“六邊形架構(gòu)”:
圖片
內(nèi)部領(lǐng)域事件的主要目標(biāo)是在領(lǐng)域間傳播信息,以實(shí)現(xiàn)業(yè)務(wù)邏輯的分離和職責(zé)隔離。
內(nèi)部領(lǐng)域事件通常使用同步或異步的方式在內(nèi)存中傳播。例如,在Java Spring中,可以使用ApplicationEventPublisher和@EventListener實(shí)現(xiàn)同步或異步的內(nèi)部領(lǐng)域事件,這些事件不會(huì)跨服務(wù)或應(yīng)用傳播。
內(nèi)部領(lǐng)域事件工作在內(nèi)存中,在設(shè)計(jì)時(shí)需要注意以下幾點(diǎn):
Spring Event 是內(nèi)部領(lǐng)域事件落地的一把利器,稍后進(jìn)行詳解。
外部領(lǐng)域事件的主要目標(biāo)是在跨服務(wù)或子域?qū)崿F(xiàn)分布式的業(yè)務(wù)邏輯和系統(tǒng)間解耦。
外部領(lǐng)域事件通常使用消息隊(duì)列(如Rocketmq、Kafka等)實(shí)現(xiàn)異步的跨服務(wù)傳播。
外部領(lǐng)域事件工作在消息中間件之上,在設(shè)計(jì)時(shí)需要注意以下幾點(diǎn):
消息中間件是 外部領(lǐng)域事件 落地的關(guān)鍵技術(shù),由于篇幅原因,在此不做過(guò)多解釋。稍后會(huì)有文章進(jìn)行詳解。
Spring Event 是 Spring Framework 中的一個(gè)模塊,幫助在應(yīng)用程序中實(shí)現(xiàn)事件驅(qū)動(dòng)。它主要用于組件之間同步/異步通信,解耦事件發(fā)布者和事件消費(fèi)者。
使用 Spring Event 包括以下步驟:
在 Spring 中,事件的處理器可以通過(guò)三種方式來(lái)實(shí)現(xiàn):
由于與 Spring 存在強(qiáng)耦合,現(xiàn)在已經(jīng)很少使用,可以直接跳過(guò)。
下面是一個(gè)基于接口的事件處理的示例代碼:
@Componentpublic class MyEventListener implements ApplicationListener<MyEvent> { @Override public void onApplicationEvent(MyEvent event) { // 處理事件 System.out.println("Received event: " + event.getMessage()); }}public class MyEvent { private String message; public MyEvent(String message) { this.message = message; } public String getMessage() { return message; }}@Componentpublic class MyEventPublisher { @Autowired private ApplicationEventPublisher eventPublisher; public void publishEvent(String message) { MyEvent event = new MyEvent(message); eventPublisher.publishEvent(event); }}
在這個(gè)示例中,MyEvent 是一個(gè)自定義的事件類,MyEventListener 是一個(gè)實(shí)現(xiàn)了 ApplicationListener 接口的監(jiān)聽器,用于處理 MyEvent 事件,MyEventPublisher 是用于發(fā)布事件的類。
當(dāng)應(yīng)用程序調(diào)用 MyEventPublisher 的 publishEvent 方法時(shí),會(huì)觸發(fā)一個(gè) MyEvent 事件,MyEventListener 中的 onApplicationEvent 方法將被自動(dòng)調(diào)用,從而處理這個(gè)事件。
Spring 提供 @EventListener 和 @TransactionListener 兩個(gè)注解以簡(jiǎn)化對(duì)事件的處理。
Spring 的 EventListener 監(jiān)聽器是一種相對(duì)于傳統(tǒng)的事件監(jiān)聽方式更為簡(jiǎn)潔和靈活的事件機(jī)制。與傳統(tǒng)的事件機(jī)制不同,EventListener 不需要顯示地繼承特定的事件接口,而是使用注解標(biāo)識(shí)需要監(jiān)聽的事件類型,然后通過(guò)一個(gè)單獨(dú)的監(jiān)聽器類處理所有類型的事件。
相比之下 EventListener 的優(yōu)勢(shì)主要有以下幾點(diǎn):
以下是一個(gè)簡(jiǎn)單的例子:
@Componentpublic class MyEventListener{ @EventListener public void onApplicationEvent(MyEvent event) { // 處理事件 System.out.println("Received event: " + event.getMessage()); }}public class MyEvent { private String message; public MyEvent(String message) { this.message = message; } public String getMessage() { return message; }}@Componentpublic class MyEventPublisher { @Autowired private ApplicationEventPublisher eventPublisher; public void publishEvent(String message) { MyEvent event = new MyEvent(message); eventPublisher.publishEvent(event); }}
相比基于接口的事件處理,EventListener 是一種更加簡(jiǎn)潔、靈活、松耦合、可測(cè)試的事件機(jī)制,能夠有效地降低開發(fā)的復(fù)雜度,提高開發(fā)效率。
在 Spring 中,TransactionEventListner 和 EventListner 都是用于處理事件的接口。不同之處在于
具體來(lái)說(shuō),在使用 Spring 的聲明式事務(wù)時(shí),可以在事務(wù)提交后觸發(fā)某些事件。這就是 TransactionEventListner 的應(yīng)用場(chǎng)景。而 EventListner 則不涉及事務(wù),可以用于在事件發(fā)布后觸發(fā)一些操作。
下面是一個(gè)簡(jiǎn)單的示例,演示了如何使用 TransactionEventListner 和 EventListner:
@Componentpublic class MyEventListener { @EventListener public void handleMyEvent(MyEvent event) { // 處理 MyEvent } @TransactionalEventListener public void handleMyTransactionalEvent(MyTransactionalEvent event) { // 處理 MyTransactionalEvent }}@Servicepublic class MyService { @Autowired private ApplicationEventPublisher eventPublisher; @Autowired private MyRepository myRepository; @Transactional public void doSomething() { // 做一些事情 MyEntity entity = myRepository.findById(1L); // 發(fā)布事件 eventPublisher.publishEvent(new MyEvent(this, entity)); // 發(fā)布事務(wù)事件 eventPublisher.publishEvent(new MyTransactionalEvent(this, entity)); }}
在這個(gè)例子中,MyEventListener 類定義了兩個(gè)方法,handleMyEvent 和 handleMyTransactionalEvent,分別處理 MyEvent 和 MyTransactionalEvent 事件。其中,handleMyTransactionalEvent 方法用 @TransactionalEventListener 注解標(biāo)記,表示它只會(huì)在事務(wù)提交后觸發(fā)。
MyService 類中的 doSomething 方法使用 ApplicationEventPublisher 來(lái)發(fā)布事件。注意,它發(fā)布了兩種不同類型的事件:MyEvent 和 MyTransactionalEvent。這兩個(gè)事件會(huì)分別觸發(fā) MyEventListener 中的對(duì)應(yīng)方法。
總的來(lái)說(shuō),Spring 的事件機(jī)制非常靈活,可以方便地?cái)U(kuò)展應(yīng)用程序的功能。TransactionEventListner 和 EventListner 這兩個(gè)接口的應(yīng)用場(chǎng)景有所不同,可以根據(jù)實(shí)際需求選擇使用。
@Async是Spring框架中的一個(gè)注解,用于將一個(gè)方法標(biāo)記為異步執(zhí)行。使用該注解,Spring將自動(dòng)為該方法創(chuàng)建一個(gè)新線程,使其在后臺(tái)異步執(zhí)行,不會(huì)阻塞主線程的執(zhí)行。
在實(shí)際應(yīng)用中,使用@Async可以大大提升應(yīng)用的并發(fā)處理能力,使得系統(tǒng)能夠更快地響應(yīng)用戶請(qǐng)求,提高系統(tǒng)的吞吐量。
@Async 和 @EventListener 或 @TransactionEventListener 注解在一起使用時(shí),會(huì)產(chǎn)生異步的事件處理器。使用這種組合的方式,事件處理器會(huì)在單獨(dú)的線程池中執(zhí)行,以避免阻塞主線程。這種方式在需要處理大量事件或者事件處理器耗時(shí)較長(zhǎng)的情況下非常有用,可以有效提升應(yīng)用的性能和可伸縮性。同時(shí),Spring 框架對(duì)這種方式也提供了完善的支持,可以方便地使用這種方式來(lái)實(shí)現(xiàn)異步事件處理。
下面是一個(gè)簡(jiǎn)單的示例代碼,演示了如何在 Spring 中使用 @Async 和 @EventListener 一起實(shí)現(xiàn)異步事件處理:
@Componentpublic class ExampleEventListener { @Async @EventListener public void handleExampleEvent(ExampleEvent event) { // 在新的線程中執(zhí)行異步邏輯 // ... }}
在這個(gè)示例中,ExampleEventListener 類中的 handleExampleEvent 方法使用了 @Async 和 @EventListener 注解,表示這個(gè)方法是一個(gè)異步事件監(jiān)聽器。當(dāng)一個(gè) ExampleEvent 事件被觸發(fā)時(shí),這個(gè)方法會(huì)被異步地執(zhí)行。在這個(gè)方法中,可以執(zhí)行任何異步的邏輯處理,比如向隊(duì)列發(fā)送消息、調(diào)用其他服務(wù)等。
備注:在使用 @Async 時(shí),需要根據(jù)業(yè)務(wù)場(chǎng)景對(duì)線程池進(jìn)行自定義,以免出現(xiàn)資源不夠的情況(Spring 默認(rèn)使用單線程處理@Async異步任務(wù))
綜上所述,當(dāng)領(lǐng)域事件發(fā)出來(lái)之后,不同的注解會(huì)產(chǎn)生不同的行為,簡(jiǎn)單匯總?cè)缦拢?span style="display:none">lQH28資訊網(wǎng)——每日最新資訊28at.com
@EventListener | @TransactionEventListener | |
無(wú) @Async | 順序、同步執(zhí)行 | 事務(wù)提交后、同步執(zhí)行 |
有 @Async | 順序、異步執(zhí)行 | 事務(wù)提交后、異步執(zhí)行 |
圖片
特點(diǎn):
應(yīng)用場(chǎng)景:
圖片
特點(diǎn):
應(yīng)用場(chǎng)景:
備注:@TransactionEventLisnter 必須在事務(wù)上下文中,脫離上下文,調(diào)用不會(huì)生效
圖片
特點(diǎn):
應(yīng)用場(chǎng)景:
圖片
特點(diǎn):
應(yīng)用場(chǎng)景:
異步處理。記錄操作日志,異步保存數(shù)據(jù)等
備注:@TransactionEventLisnter 必須在事務(wù)上下文中,脫離上下文,調(diào)用不會(huì)生效。
領(lǐng)域事件是系統(tǒng)中的解耦利器,包括:
Spring Event 是實(shí)現(xiàn)內(nèi)部領(lǐng)域事件解耦的利器,基于 事件監(jiān)聽注解 和 同步/異步 兩組注解的組合為不同的應(yīng)用場(chǎng)景提供不同的支持。
@EventListener | @TransactionEventListener | |
無(wú) @Async | 順序、同步執(zhí)行 | 事務(wù)提交后、同步執(zhí)行 |
有 @Async | 順序、異步執(zhí)行 | 事務(wù)提交后、異步執(zhí)行 |
外部領(lǐng)域事件 強(qiáng)依賴于消息中間件的使用,稍后會(huì)有文章進(jìn)行詳解。
本文鏈接:http://www.tebozhan.com/showinfo-26-11898-0.html解密DDD:領(lǐng)域事件這一系統(tǒng)解耦的終極武器
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com
上一篇: 快速掌握 Go 工作區(qū)模式