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