CQRS本身就是一個非常簡單的模式。他只是規定了應用程序的命令處理的組件(增,刪,改)應該和查詢的組件進行分離。盡管這種分離模式本身非常簡單,但是當與其他模式結合在一起時,它就提供了許多非常強大的功能。Axon提供了可以配合CQRS使用且很輕松地實現各種模式的構建塊。
下圖顯示了基于CQRS的事件驅動架構的擴展布局示例。顯示在左側的UI組件以兩種方式與應用程序的其余部分進行交互:向應用程序發送命令(如頂部分所示),并向應用程序查詢信息(如底部分所示)。
命令(Command)通常由比較簡單的對象來表示,這些對象包含了command handler(處理command的程序)要處理該命令所需的所有數據。一個命令通常用它的名字來表達它的意圖。用Java語言來說,這意味著類名是用來表明需要做什么的,而類里的字段則提供所需的數據。
當Command Bus(命令總線)收到command的時候會把他路由到對應的Command Handlers。每個Command Handler只會接收他指定類型的command并根據command里的內容來執行相應的業務邏輯。然而,在某些情況下,無論命令的實際類型(如驗證,日志記錄或授權)是什么,他還是要執行對應的邏輯。
Command Handler從repository (倉儲,在DDD中他們將持久化數據的地方抽象出的一個概念)獲取到領域對象(Aggregates,聚合),然后在領域對象執行對應的方法(method)從而來改變它們的狀態。這些聚合通常包括具體的實際業務邏輯,因此他們須要維護自己內部的數據一致性。聚合的狀態變化會導致產生領域事件。領域模型由領域事件和聚合組成。
倉儲(repository)負責提供對聚合根的訪問。通常情況下,這些倉儲庫只會針對通過其唯一標識符來查找聚合進行優化。一種方式是,倉儲存儲的是聚合本身的狀態(例如使用對象關系映射),而另一種是,倉儲通過Event Store存儲聚合在各種行為中產生的真實事件。倉儲還負責持久化聚合產生的事件。
Axon提供了對直接持久化聚合(例如使用對象關系映射方式)和事件溯源 (event sourcing)的支持。
事件總線(Event bus)會將事件分發給所有感興趣的事件監聽者。他可以是同步方式也可以是異步方式。異步方式允許事件發送出去后就直接返回并將控制權交給用戶,而此時的后臺應用程序也會對事件進行處理。這樣好處就是用戶無需等待事件處理完成后才能做其他的事,所以應用程序的響應速度就會更快。另一方面,同步事件處理更簡單且符合常理。默認情況下,同步處理方式會將command的處理和event的處理放進同一個事務中。
Event listeners接收事件并處理它們。-些hanndler的操作是更新數據源的數據,而另一些是將消息發送到其他外部系統。您可能會注意到,command handlers完全不知道接下來會發生什么事。這意味著對擴展應用程序新功能是非入侵式的。所以你需要做的是添加另一個事件監聽器(event listener)。事件將應用程序中的所有組件很松散地耦合在一起。
在某些情況下,事件處理需要將新命令發送給應用程序。例如,當我們收到一個訂單,這可能意味著客戶的賬戶應該扣掉買商品的錢,并且必須告知運送方準備運送所購買的商品。在許多應用中,邏輯將變得比這更復雜:如果客戶沒有及時付款呢?您會立即寄出貨物,還是先等待付款?saga是CQRS的概念,它負責管理這些復雜的業務事務。
Axon 框架從3.1版本開始提供了組件來處理查詢(query)。查詢總線(Query Bus)收query并將其路由到查詢處理Handler(Query Handlers)。query handler會到查詢總線上注冊,同時會帶上他能處理的query類型及響應類型。查詢和結果類型通常都是簡單的只讀DTO對象。這些DTO的內容通常由用戶界面的決定。在大多數情況下,它們直接映射到UI中的特定視圖(也稱為“按視圖”)。
可以為相同類型的查詢和響應類型注冊多個Query handler。當分發query的時候,客戶端可以指定他是想要一個結果還是來自所有可用的query handlers的結果。
Axon模塊結構
Axon Framework包含了很多模塊來應對CQRS的特定問題領域。根據你項目的使用情況,可以自己選擇一個或者多個模塊。
從Axon2.1起,所有模塊兼容OSGi。這意味著它們在manifest文件包含所需的頭文件,并聲明它們導入和導出的包。目前,只有Slf4J bundle(1.7.0 <= 版本< 2.0.0)是必需的。所有其他導入的都被標記為可選的。
主要模塊
Axon的主要模塊是經過全面測試的模塊,足夠堅固,可用于苛刻的生產環境。所有這些模塊的maven groupId是org.axonframework。
Core模塊正如其名一樣,他是Axon的核心模塊。如果您使用單節點設置,則該模塊可能會提供您需要的所有組件。所有其他的Axon模塊都依賴于這個模塊,所以它必須始終在classpath可用。
Test模塊包含測試功能,可用于測試基于Axon的組件,如Command Handlers, Aggregates and Sagas。您通常在運行時不需要此模塊,只需在測試期間將其添加到classpaths中即可。
分布式CommandBus模塊包含可用于在多個節點上分發命令的實現。它使用JGroups和Spring Cloud Connectors來連接其他節點。
AMQP模塊提供的組件允許您使用基于AMQP的消息中間件作為分發機制來構建EventBus。即使Event Handler暫時不可用,也可以保證傳送。
Spring模塊允許在Spring應用程序上下文中配置Axon組件。它還提供了許多特定于Spring Framework的構建塊實現,例如用于在Spring消息通道上發布和檢索Axon事件的適配器。
MongoDB是一個基于文檔的NoSQL數據庫。 Mongo模塊提供了Event和Saga Store實現,它們將事件流和saga存儲在MongoDB數據庫中。
幾個AxonFramework組件提供監視信息。Metrics模塊提供了基于Codehale的基本實現來收集監控信息。
使用Axon API
CQRS是一種架構模式,因此無法提供適合所有項目的單一解決方案。很明顯,Axon框架并不試圖提供這種解決方案。取而代之的是,Axon提供了遵循最佳實踐的實現方法,以及根據您的具體要求調整每個實現的方法。
幾乎所有的基礎設施構建塊都會提供鉤子點(例如攔截器,解析器等),允許您將特定于應用程序的行為添加到這些構建塊。在很多情況下,Axon將為那些適合大多數用例的鉤子點提供實現。如果需要的話,你可以簡單地實現你自己的。
非基礎設施對象,如消息,通常是不可變的。這確保了這些對象可以安全地在多線程環境中使用,沒有副作用。
為確保定制最大化,所有的Axon組件都使用接口進行定義。抽象和具體的實現類你可以根據你自己的情況選擇使用,不會影響到框架的依賴。你甚至可以使用該接口來構建你自己想要的實現。
Spring支持
Axon框架為Spring提供了廣泛的支持,但強制您使用Axon的時候也必須要用Spring。所有組件都可以以編程方式進行配置,并且不需要把Spring放在classpath路徑中。但是,如果你使用Spring,使用Spring的注解支持,許多配置變得更加容易。