CQRS & Event Sourcing — 解決檢索應用程序狀態問題的一劑良方

現在,每個開發人員都很熟悉MVC標準體系結構設計模式。大多數的應用程序都是基於這種體系結構進行創建的。它允許我們創建可擴展的大型企業應用程序,但近期我們還聽到了另外的一些有關於CQRS/ES的相關信息。這些方法應該被放在MVC中一起使用嗎?他們可以解決什麼問題?現在,讓我們一起來看看CQRS/ES是什麼,以及他們都有哪些優點和缺點。

CQRS — 模式介紹

CQRS(Command Query Responsibility Segregation)是一種簡單的設計模式。它衍生與CQS,即命令和查詢分離,CQS是由Bertrand Meyer所設計。按照這一設計概念,系統中的方法應該分為兩種:改變狀態的命令和返回值的查詢。Greg young將引入了這個設計概念,並將其應用於對象或者組件當中,這就是今天所要將的CQRS。它背後的主要思想是應用程序更改對象或組件狀態(Command)應該與獲取對象或者組件信息(Query)分開。

下面,將通一張圖來說明應用程序中有關CQRS部分的組成結構:

CQRS & Event Sourcing — 解決檢索應用程序狀態問題的一劑良方

Commands(命令)—表示用戶的操作意圖。它們包含了與用戶將要對系統執行操作的所有必要信息。

  • Command Bus(命令總線):是一種接收命令並將命令傳遞給命令處理程序的隊列。Command Handler(命令處理程序):包含實際的業務邏輯,用於驗證和處理命令中接收到的數據。Command handler負責生成和傳播域事件(Event)到事件總線(Event Bus)。Event Bus(事件總線):將事件發佈給訂閱特定事件類型的事件處理程序。如果存在連續的事件依賴,事件總線可以使用異步或者同步的方式將事件發佈出去。Event Handler(事件處理程序):負責處理特定類型的事件。它們的職責是將應用程序的最新狀態保存到讀庫中,並執行終端的相關操作,如發送電子郵件,存儲文件等。

Query(查詢):表示用戶實際可用的應用程序狀態。獲取UI的數據應該通過這些對象完成。

下面我們將介紹有關CQRS的諸多優點,它們是:

  • 我們可以給處理業務邏輯部分和處理查詢部分的開發人員分別分配任務,但需要小心的是,這種模式可能會破壞信息的完整性。通過在多個不同的服務器上擴展Commands和Query,我們可以進一步提升應用程序的讀/寫性能。使用兩個不同的數據庫(讀庫/寫庫)進行同步,可以實現自動備份,無需額外的干預工作。讀取數據時不會涉及到寫庫的操作,因此在使用事件源是讀數據操作會更快。我們可以直接為視圖層構建數據,而無需考慮域邏輯,這可以簡化視圖層的工作並提高性能。

儘管使用CQRS模式具有上述諸多的優點,但是在使用前還需要慎重考慮。對於只具有簡單域的簡單項目,其UI模型與域模型緊密聯繫的,使用CQRS反而會增加項目的複雜度和冗餘度,這無疑是過度的設計項目。此外,對於數據量較少或者性能要求較低的項目實施CQRS模式不會帶來顯著的性能提升。

Event Sourcing — 案例研究

有這樣一個案例,我們想要檢索任何一個域對象的歷史狀態數據,而且在任何時間都可以生成統計數據。我們想要檢查上個月、上個季度或者過去任何時間的狀態彙總。想要解決這個問題並不容易。我們可以在特定的時間範圍內將額外的數據保存在數據庫中,但這種方法也存在一些缺點。我們不知道範圍應該是什麼樣子,以及未來統計數據需要哪些數據項。為了避免這些問題,我們可以每天為所有聚合創建快照,但它們同樣會產生大量的冗餘數據。

Event Sourcing(ES)似乎是目前解決這些問題的最佳方案。Event Sourcing允許我們將Aggregate(聚合)狀態的每一個更改事件保存在Event Store的事件存儲庫中。通過Command Handler將事件寫入到事件存儲庫中,並處理相關的邏輯。要創建Aggregate(聚合)對象的當前狀態,我們需要運行創建預期域對象的所有事件並對其執行所有的更改。下面我們將通過一張圖來說明這一架構設計方式:

CQRS & Event Sourcing — 解決檢索應用程序狀態問題的一劑良方

下面我們將列舉一些使用ES的優點:

  • 時間穿梭機:可以及時重建特定聚合的狀態。每個事件都包含一個時間戳。根據這些時間戳可以在特定的時間內運行事件或者停止事件。自動審計:我們不需要額外的工作就可以檢查出在特定的時間範圍內誰做了什麼以及改變了什麼。這和可以顯示更改歷史記錄的系統日誌不同,事件可以告知我們每次更改背後所對應的操作意圖。易於引入糾正措施:當數據庫中的數據發生錯誤時,我們可以將應用程序的狀態回退到特定的時間點上,並重建當時的應用程序狀態。易於調試:如果應用程序出現問題,我們可以將特定事件內的所有事件取出,並逐條的重建應用狀態,以檢查應用程序可能出現問題的地方。這樣我們可以更快的找到問題,縮短調試所需的時間。

Aggregates

Aggregate(聚合)一詞在本文中多次被提及,那它到底是什麼意思?Aggregate(聚合)來自於領域驅動設計(DDD)的一個概念,它指的是始終保持一致狀態的實體或者相關實體組。我們可以簡單的理解為接收和處理Command(包含Command Handler)的一個邊界,然後根據當前狀態生成事件。在通常情況下,Aggregate root(聚合根)由一個域對象構成,但它可以由多個對象組成。我們還需要注意整個應用程序可以包含多個Aggregate(聚合),並且所有事件都存儲在同一個存儲庫中。

總結

CQRS/ES可以作為特定問題的解決方案。它可以在標準N層架構設計的應用程序的某些層中進行引入,它可以解決非標準問題,常規架構中我們所拿到的是最終狀態,在很多情況下,固然當前狀態很重要,但我們還需要知道當前狀態是如何產生的。CQRS和ES兩種概念應該一起使用嗎?事實表明,並沒有。我們想要統計任何時間範文內的域對象狀態,而寫庫只能存儲當前狀態。引入CQRS並沒能幫助我們解決這一問題。在下一章節中,我們將引入Axon框架,Axon框架時間了CQRS/ES,用於解決某些域對象的一些特定問題,尤其是收集歷史統計數據。我們將闡述如何使用Axon框架實現CQRS/ES並實現與Spring Boot應用程的整合。

鏈接:https://www.nexocode.com/blog/posts/cqrs-and-event-sourcing/

譯文:(譯)CQRS & Event Sourcing - 解決檢索應用程序狀態問題的一劑良方


分享到:


相關文章: