微服務架構談(4): 領域事件 - 解耦微服務的關鍵
那到底什麼是領域事件?領域事件驅動設計的技術實現機制是怎樣的?我們這一章將重點講解這兩個問題。
1 領域事件
領域事件是領域模型非常重要的一部分,用於表示領域中發生的事件。一個領域事件往往會導致進一步的業務操作,它在實現領域模型解耦的同時,還有助於形成完整的業務操作閉環。
舉例來說,領域事件可以是業務流程的一個步驟,比如投保業務繳費完成後,觸發投保單轉保單的動作;也可以是定時批處理過程中發生的事件,比如批處理生成季繳保費通知單,觸發繳費郵件通知操作;還可以是一個事件發生後觸發的後續動作,比如密碼連續輸錯三次,觸發鎖定賬戶的動作。
那在領域建模時,如何識別和捕捉這些領域事件呢?
在用戶旅程或者場景分析時,我們需要捕捉業務人員、需求分析人員以及領域專家口中的這些具有前後動作關係的關鍵詞,比如:“如果發生⋯⋯,則⋯⋯”“當做完⋯⋯時,請通知⋯⋯”“發生⋯⋯時,則⋯⋯” 等。在這些業務場景中,如果發生某種事件後,會觸發進一步的業務操作,那麼這個事件很可能就是領域事件。
領域事件採用事件驅動架構(Event-Driven Architecture,EDA)設計,可以切斷領域模型之間的強依賴關係,在領域事件發佈後,事件發佈方不必關心訂閱方的事件處理是否成功。這樣就可以實現領域模型的解耦,維護領域模型的獨立性。當領域模型映射到微服務時,領域事件就可以解耦微服務,這時微服務之間的數據就可以不再要求強一致性,而是基於最終一致性。
再回到具體的業務場景,我們發現有的領域事件發生在微服務內的聚合之間,有的發生在微服務之間,還有兩者皆有的場景。一般來說,跨微服務的領域事件會相對較多。在微服務設計時,不同場景下的領域事件的處理方式會不同。
與採用同步服務調用實現數據強一致性的機制不同,領域事件一般都會結合消息中間件和事件發佈訂閱的異步處理方式,實現數據最終一致性。
那麼,領域事件處理爲什麼要採用最終一致性,而不是強一致性呢?
我們先一起回顧一下第 8 章的內容,聚合有一個重要設計原則:“在邊界之外使用最終一致性。” 如果在一次事務提交中,修改的數據超出了一個聚合的邊界,簡單點說就是一筆交易,如果同時涉及多個聚合的數據更新,那麼就可以採用數據最終一致性。
1.1 微服務內的領域事件
在微服務內發生領域事件,如果同時更新多個聚合數據時,你需要確保多個聚合數據的一致性。按照 DDD“一次事務只更新一個聚合” 的原則,你可以引入事件總線(Event Bus),通過事件總線來實現微服務內多聚合數據的最終一致性,或者採用事務機制保證數據強一致性。
在採用事件總線進行領域事件處理時,可以根據需要完成領域事件實體的構建和事件數據持久化,然後發佈方聚合會將領域事件數據發佈到事件總線,由訂閱方聚合接收領域事件數據後完成後續業務處理。事件總線的設計方式,可能會增加微服務開發的複雜度,需要結合應用的複雜度和收益進行綜合考慮。
你可能會問,在同一個微服務內,爲什麼一次事務更新多個聚合數據時,要用事件總線或事務機制呢?
這是因爲聚合是微服務內最小的業務功能單元。爲了保證聚合內數據更新時符合聚合內固定的業務規則,在一次事務提交時通常會將聚合內所有變更的對象數據作爲整體,通過聚合領域服務或聚合根方法一次通過倉儲完成數據持久化操作。如果在一次交易中需要同時更新多個聚合數據,那麼每一個聚合就是一個獨立的數據提交單元,我們需要確保多個聚合數據都能在這個交易中成功提交併更新,以保證不同聚合數據的一致性。而基於事件總線的異步化機制,就可以保證微服務內聚合之間數據提交時的最終一致性。
如果不採用事件總線的最終數據一致性機制,其實你也可以採用事務機制保證數據強一致性。比如在應用服務中增加事務控制,在對多個聚合的領域服務進行組合和編排時,通過事務機制來確保多個聚合在提交數據時實現數據強一致性。這種方式一般應用於實時性和數據一致性要求高的業務場景,但採用事務機制可能會出現系統性能損耗。
1.2 微服務之間的領域事件
跨微服務的領域事件可以在不同限界上下文,或領域模型之間實現業務協作,其主要目的是實現微服務解耦,推動業務流程或者數據在不同子域或微服務之間流轉。同時也可以減輕微服務之間同步服務訪問的壓力,避免當某個關鍵微服務無法提供服務時,出現雪崩效應。
領域事件發生在微服務之間的場景比較多,事件處理的機制也更加複雜。微服務之間的領域事件可以採用異步化的最終一致性設計。設計時要總體考慮領域事件的構建、發佈和訂閱、領域事件數據的持久化、消息中間件,甚至在事件數據持久化時可能還需要引入分佈式事務等機制。
微服務之間的領域事件,也可以採用同步服務調用的強一致性設計,實現實時的數據和服務訪問。其弊端就是需要引入分佈式事務機制,以確保微服務之間的數據強一致性。但分佈式事務機制會影響系統性能,同時增加微服務之間的耦合。
所以,應儘量減少微服務之間的同步服務調用方式,優先採用基於消息中間件的最終一致性設計。
2 領域事件案例
下面介紹一個與保險承保業務有關的領域事件的案例,以加深對領域事件的理解。
一個保單的生成,通常會經歷很多業務子域、業務狀態變更和跨微服務業務數據的傳遞。這個過程會產生很多領域事件,這些領域事件促成了保險業務數據、對象在不同的微服務和子域之間的流轉和角色轉換,如不同微服務或聚合之間的實體與值對象之間的轉換。在圖 9-1 中,我列出了幾個關鍵流程,用來說明如何用領域事件驅動設計來驅動保險業務流程。
2)收款微服務繳費完成後,發佈第二個領域事件:繳費已完成。將繳費事件數據發佈到消息中間件。原來的事件訂閱方收款微服務這時則變成了事件發佈方。原來的發佈方投保微服務這時轉換爲繳費已完成事件的訂閱方。投保微服務在收到繳費信息並確認繳費完成後,完成投保單轉成保單的操作。繳費已完成領域事件結束。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s?__biz=MzIxMzEzMjM5NQ==&mid=2651043651&idx=1&sn=f465e0b1a7e9148095167541ad7ec431&chksm=8c4c6247bb3beb51a3658248e0a42c382441c4c601ade5ad53164a4f8729eba7f54bf0251ca4&scene=178&cur_album_id=1343963749467717633#rd