總算講清DDD領域驅動設計中的領域事件(Domain Event)了

學習什麼是領域事件.何時而且爲何要使用領域事件。 •學習如何將領域事件建模成對象,什麼時候應該爲領域事件建立惟一的身份標識。 •學習一個輕量級的發佈-訂閱[Gamma et al]模式。 •學習哪些組件用於發佈事件,哪些組件用於訂閱事件。 •學習爲何咱們須要一個事件存儲.如何實現事件存儲、如何使用事件存儲。 •學習S aaSOvation團隊是如何經過不一樣的方式將領域事件發佈給自治系統前端

1 什麼時候、爲何使用領域事件?

1.1 定義

使用領域事件來建模發生在領域中的一些事情。這是一個功能強大的建模工具,讓人愛不釋手。 使用領域事件時,首先就是要對不一樣事件進行定義。數據庫

《領域驅動設計》一書中並未給出領域事件的定義。由於該模型是在該書出版後才被提出。 當前對領域事件的定義:領域專家所關心的發生在領域中的一些事件。 將領域中所發生的活動建模成一系列的離散事件。每一個事件都用領域對象來表 示……領域事件是領域模型的組成部分,表示領域中所發生的事情。[Evans, Ref, P-20] 一markdown

個領域事件將致使進一步的業務操做,在實現業務解耦的同時,還有助於造成完整的業務閉環。網絡

領域事件能夠是業務流程的一個步驟,好比一個事件發生後觸發的後續動做,好比密碼連續輸錯三次,觸發鎖定帳戶的動做。架構

1.2 識別領域事件

  • 「若是發生……,則……」
  • 「當作完……的時候,請通知……」(這裏的通知自己並不能構成一個事件,而只是代表咱們須要向外界發出通知)

在這些場景中,若是發生某種事件後,會觸發進一步的操做,那麼這個事件極可能就是領域事件。因爲領域事件須要發佈到外部系統,好比發佈到另外一個限界上下文。因爲這樣的事件由訂閱方處理,它將對本地和遠程上下文產生深遠的影響。併發

那領域事件爲何要用最終一致性,而不是傳統SOA的直接調用?異步

聚合的一個原則:一個事務中最多隻能更改一個聚合實例。因此分佈式

  • 本地限界上下文中的其餘聚合實例即可以經過領域事件的方式同步
  • 用於使遠程依賴系統與本地系統保持一致。解耦本地系統和遠程系統還有助於提升雙方協做服務的可伸縮性。

聚合建立併發布事件。訂閱方能夠先存儲事件,而後再將其轉發到遠程訂閱方,或不經存 儲,直接轉發。除非MQ共享了模型的數據存儲,否則即時轉發須要XA(兩階段提交)。微服務

考慮在系統非高峯時期,批處理過程一般進行一些系統維護工做,好比刪除過時對象、建立新對象以支持新業務需求或通知用戶所發生的重要事件。這樣的批處理過程一般需複雜 查詢且需龐大事務支持。若這些批處理過程存在冗餘會怎麼樣? 系統中發生的每一件事情,咱們都用事件形式捕獲,而後將事件發佈給訂閱方處理,能簡化系統嗎?確定的!它可消除先前批處理過程當中的複雜查詢,由於咱們可以準確知道在什麼時候發生何事,限界上下文也由此知道接下來應該作啥。在接收到領域事件時,系統可當即處理。本來批量集中處理的過程能夠分散成許多粒度較小的處理單元,業務需求也由此更快知足,用戶也可及時進行下一步操做。工具

領域事件驅動設計可切斷領域模型之間的強依賴。 事件發佈完成後,發佈方沒必要關心後續訂閱方事件處理是否成功,便可實現領域模型的解耦,維護領域模型的獨立性和數據一致性。 在領域模型映射到微服務架構時,領域事件可解耦微服務,微服務間的數據沒必要要求強一致性,而是基於事件的最終一致性。

觸發領域事件

領域事件由外部命令觸發。觸發命令能夠是領域服務,也能夠是實體的某一個方法或者行爲。

觸發事件的用法

走canal增量同步數據庫數據,經過監聽特定表的數據變動來觸發生成事件的調用。如此有利於主流業務的解耦,提升維護和可讀性。(具體生成事件的操做固然仍是放在對應領域的微服務中,canal監聽消費端能夠理解爲一個任務調度平臺)。這樣的實現邏輯相對簡單。

那不一樣領域事件,如何處理呢?

3 處理領域事件

3.1 微服務內

領域事件發生在微服務內的聚合間,領域事件發生後完成事件實體的構建和事件數據持久化,發佈方聚合將事件發佈到事件總線,訂閱方接收事件數據完成後續業務操做。

微服務內大部分事件的集成,都發生在同一進程,進程自身便可控制事務。但一個事件若同時更新多個聚合,按一次事務只更新一個聚合原則,可考慮引入事件總線。

微服務內應用服務,可經過跨聚合的服務編排和組合,以服務調用方式完成跨聚合訪問,這種方式一般應用於實時性和數據一致性要求高的場景。這個過程會用到分佈式事務,以保證發佈方和訂閱方的數據同時更新成功。

在微服務內,不是說少用領域事件,而是推薦少用事件總線。DDD是以聚合爲單位進行數據管理,若一次操做會修改同一微服務內的多個聚合的數據,就需保證多個聚合的數據一致性。 爲了解耦不一樣聚合,需採用分佈式事務或事件總線,而事件總線不太方便管理服務和數據的關係,可用相似saga之類的分佈式事務技術。總之需確保不一樣聚合的業務規則和數據一致性。

3.2 微服務間

跨微服務的領域事件會在不一樣限界上下文或領域模型間實現業務協做,主要爲解耦,減輕微服務間實時服務訪問壓力。

領域事件發生在微服務間較多,事件處理機制也更復雜。跨微服務事件可推進業務流程或數據在不一樣子域或微服務間直接流轉。

跨微服務的事件機制要整體考慮事件構建、發佈和訂閱、事件數據持久化、MQ,甚至事件數據持久化時還可能需考慮引入分佈式事務。

微服務間訪問也可採用應用服務直接調用,實現數據和服務的實時訪問,弊端就是跨微服務的數據同時變動須要引入分佈式事務。分佈式事務會影響系統性能,增長微服務間耦合,儘可能避免使用。

5 領域事件設計

5.1 構建和發佈

基本屬性

至少包括以下:

  • 事件惟一標識(全局惟一,事件可以無歧義在多個限界上下文中傳遞)
  • 發生時間
  • 事件類型
  • 事件源

即主要記錄事件自己以及事件發生背景的數據。

業務屬性

記錄事件發生那刻的業務數據,這些數據會隨事件傳輸到訂閱方,以開展後續業務操做。

事件基本屬性和業務屬性一塊兒構成事件實體,事件實體依賴聚合根。領域事件發生後,事件中的業務數據再也不修改,所以業務數據能夠以序列化值對象的形式保存,這種存儲格式在消息中間件中也比較容易解析和獲取。

爲保證事件結構的統一,一般建立事件的基類,子類可自行繼承擴展。因爲事件沒有太多業務行爲,實現通常比較簡單。

事件發佈前需先構建事件實體並持久化。 事件實體的業務數據推薦按需發佈,避免泄露沒必要要業務信息。

事件發佈方式

  • 可經過應用服務或者領域服務發佈到事件總線或MQ
  • 也可從事件表中利用定時程序或數據庫日誌捕獲技術獲取增量事件數據,發佈到MQ

5.2 事件數據持久化

意義

  • 系統之間數據對帳
  • 實現發佈方和訂閱方事件數據的審計

當遇到MQ、訂閱方系統宕機或網絡中斷,在問題解決後仍可繼續後續業務流轉,保證數據一致性。 畢竟雖然MQ都有持久化功能,但中間過程或在訂閱到數據後,在處理以前出問題,須要進行數據對帳,這樣就無法找到發佈時和處理後的數據版本。關鍵的業務數據推薦仍是落庫。

實現方案

  • 持久化到本地業務DB的事件表,利用本地事務保證業務和事件數據的一致性
  • 持久化到共享的事件DB。業務、事件DB不在同一DB,它們的數據持久化操做會跨DB,所以需分佈式事務保證業務和事件數據強一致性,對系統性能有影響

5.3 事件總線(EventBus)

意義

實現同一微服務內的聚合之間的領域事件,提供事件分發和接收等服務。 是進程內模型,會在微服務內聚合之間遍歷訂閱者列表,採起同步或異步傳遞數據。

由於在微服務內部在同一個進程,事件總線相對好配置,它能夠配置爲異步的也能夠配置爲同步的。若是是同步就不須要落庫。推薦少用微服務內聚合之間的領域事件,它會增長開發複雜度。 而微服務之間的事件,在事件數據落庫後,經過應用服務直接發佈到MQ。

事件分發流程

  • 如果微服務內的訂閱者(其它聚合),則直接分發到指定訂閱者
  • 微服務外的訂閱者,將事件數據保存到事件庫(表)並異步發送到MQ
  • 同時存在微服務內和外訂閱者,則先分發到內部訂閱者,將事件消息保存到事件庫(表),再異步發送到MQ

5.4 MQ

跨微服務的領域事件大多會用到MQ,實現跨微服務的事件發佈和訂閱。 雖然MQ自身有持久化功能,但中間過程或在訂閱到數據後,在處理以前出問題,須要進行數據對帳,這樣就無法找到發佈時和處理後的數據版本。關鍵的業務數據推薦仍是落庫。

5.5 接收&&處理

微服務訂閱方在應用層採用監聽機制,接收MQ中的事件數據,完成事件數據的持久化後,就能夠開始進一步的業務處理。領域事件處理可在領域服務中實現。

  • 事件是否被消費成功(消費端成功拿到消息或消費端業務處理成功),如何通知消息生產端?

由於事件發佈方有事件實體的原始的持久化數據,事件訂閱方也有本身接收的持久化數據。通常能夠經過按期對帳的方式檢查數據的一致性。

  • 在採起最終一致性的狀況下,事件消費端若是出現錯誤,消費失敗,但以前的業務都成功了,雖然記錄了event dB,但後續如何處理,人工介入嗎?若是人工介入再解決,前端用戶會不會看到數據不一致,體驗很差?

失敗的狀況應該比例是不多的。失敗的信息可採用屢次重試,若是這個還解決不了,只能將有問題的數據放到一個問題數據區,人工解決。固然要確保一個前提,要保證數據的時序性,不能覆蓋已產生的數據。

通常發佈方不會等待訂閱方反饋結果。發佈方有發佈的事件表,訂閱方有消費事件表,可採用對帳方式發現問題數據。

管理

大型系統的領域事件有不少:

  • 作好源端和目的端數據的對帳處理,發現並識別處理過程當中的異常數據

異步的方式通常都有源端和目的端按期對帳的機制。好比採用相似財務衝正的方式。若是在發佈和訂閱之間事件表的數據發現異步數據有問題,須要回退,會有相應的代碼進行數據處理,不過不一樣的場景,業務邏輯會不同,處理的方式會不同。有的甚至還須要轉人工處理。

  • 發現異常數據後,要有相應的處理機制
  • 選擇適合本身場景的技術,保證數據正確傳輸

6 總結

領域事件在設計時咱們要重點關注領域事件,用領域事件來驅動業務的流轉,儘可能採用基於事件的最終一致,下降微服務之間直接訪問的壓力,實現微服務之間的解耦,維護領域模型的獨立性和數據一致性。

領域事件驅動機制可實現一個發佈方N個訂閱方的模式,這在傳統的直接服務調用設計中基本是不可能作到的。

領域事件 V.S CQRS

CQRS主要是想讀寫分離,將沒有領域模型的查詢功能,從命令中分離出來。領域事件主要目的仍是爲了微服務解耦,在連續的業務處理過程當中,以異步化的方式完成下一步的業務處理,下降微服務之間的直連。 它們的共同點就是經過消息中間件實現從源端數據到目的端數據的交互和分離。

若是你就是不想用領域事件,聚合之間還能夠經過應用層來協調和交互。應用服務是全部聚合之上的服務,負責服務的組合和編排,以及聚合之間的協調。

參考

  • 《實現領域驅動設計》
  • 《領域驅動設計》
相關文章
相關標籤/搜索