前言
ENode是一個應用開發框架,為開發人員提供了一整套基於DDD+CQRS+ES+EDA架構風格的解決方案。ENode從發布1.0開始到現在的差不多兩年時間,我幾乎每周都在更新設計或實現代碼。以至於從來沒有一個穩定的版本可以提供給大家,非常慚愧。但我相信,隨着時間的推移和我的努力的積累,ENode一定會越來越穩定和成熟的。我覺得我此刻很幸福,因為我有自己的興趣且有機會在業余時間為了自己的興趣而奮斗。
ENode開源地址:https://github.com/tangxuehua/enode
今天是個開心的日子,因為我終於使用ENode開發出了一個比較有說服力的真實案例,一個在線會議位置管理與訂購系統。該案例與微軟的Microfost CQRS Journey案例的功能一致,只是是用ENode開發完成的。目的是為了展示:
- 如何使用ENode支持DDD領域層的實現;
- 如何使用ENode實現CQRS+ES的架構;
- 如何使用ENode實現事件驅動的架構(EDA);
另外,在案例開發過程中,也不斷發現了ENode, EQueue的很多問題。所以,通過做這個案例,也幫助ENode, EQueue完善了很多。真是要實踐才能進步啊!
ENode架構簡介
ENode目前的架構已經和最初的1.0版本有較大的差別了。比如,沒有了對Redis的依賴,增加了Command Store的設計。最新版本的ENode架構圖如下:
熟悉CQRS架構的人應該對這個圖不太陌生。需要強調的有兩點:
- 新版的架構圖中,Domain Aggregate是常駐內存的;聚合根的職責就是封裝狀態、業務規則,同時產生領域事件;
- 新增了一個Command Store,用於實現Command的冪等處理;
關於圖中的其他部分的介紹,請參看我之前寫的關於ENode系列的文章。
ENode關鍵特性
- 實現了CQRS架構,支持分布式,基於隊列的水平擴展,框架設計之初就考慮了架構的各個節點的水平擴展;
-
面向高並發設計;架構層面,充分考慮到了如何解決並發問題;通過用EQueue實現服務器之間的消息路由、通過單機內部實現類似Actor Mailbox的設計,從而避免並發的產生。提高CQRS架構C端的整體寫入吞吐量;即便在某些極端情況下出現並發問題,也支持自動的樂觀檢測,並自動重試;另外,架構層面,規定一個command只能涉及一個聚合根的修改,從而規范應用開發者必須嚴格按照消息驅動的思路來實現復雜的業務流程,而不能使用工作單元的方式,以事務的方式實現數據的強一致性;
- 嚴謹的消息冪等處理支持;由於ENode是消息驅動的架構,所以對消息的冪等性處理,是框架關注的一個重要的方面,並在架構層面做了支持。這使得應用開發人員不必擔心消息的重復處理帶來的問題。
- Domain Aggregate常駐內存;這個設計可以提高C端command的處理性能。因為我們不必像傳統的方式那樣先把聚合根從db取出來,再修改,再保存回去了;
- 框架為開發人員展示了如何更好的實現Sagas,在CQRS架構中,Sagas指基於消息驅動的業務流程;一個Saga包含若干個聚合根以及一個(通常)流程管理器(Process Manager);聚合根維護流程中的所有參與者對象的狀態以及流程本身的狀態,流程管理器負責定義和實現流程控制邏輯,無狀態;流程管理器的實現是通過響應事件、異常、應用層消息,然后發送相應的命令,從而實現消息驅動的流程;
- 采用Event Sourcing(簡稱ES)的方式來持久化聚合根狀態;通過ES實現聚合根狀態的還原以及通用的並發控制,通過聚合根ID+事件版本號作為唯一索引的思路實現樂觀並發控制;
- 消息除了command, domain event外,還支持exception message, application message;這兩種消息是我們在遇到有些消息不適合用domain event來表達時需要使用到;比如聚合里有時我們要修改某個狀態前會先做業務規則的檢查判斷,如果不合法,通常會拋異常,然后這個異常我們又希望可以讓流程管理器知道,從而流程管理器可以做后面的回滾或補償措施;另外,我們有時可能會在應用層面(command handler里)和其他外部系統發生交互,那交互的結果,使用應用層的消息更合理;ENode在架構層面,對上面這些消息做了統一的支持;
- 框架提供返回單個command執行結果以及阻塞等待command執行結果的支持,這個對於開發者希望同步執行command並知道command執行結果的時候,非常有幫助;這樣開發者就不用自己去輪訓了。
- 除了對消息的冪等處理的支持外,ENode對domain event的順序處理,也做了充分的考慮和支持,確保當C端產生的domain event同步到Q端時,Q端處理時,框架層面能確保Q端的處理順序不會亂序。從而保證C,Q兩端的數據是最終一致的;
- 保證消息能至少被處理一次;主要思路是確保消息的:1)持久化;2)消息ACK后才認為已消費;
- 支持一個聚合根一次可以產生多個domain event;雖然大部分情況,一個聚合根一次只會產生一個domain event,但有些場景,可能會產生兩個或多個。此時,這些事件會以一個事件流的方式一起apply到當前聚合根;同時,在Q端,框架也會通過必要的手段,與用戶代碼一起保證了事件流中的事件的處理順序的正確性;
- ENode所使用的分布式消息隊列EQueue,參考了阿里的RocketMQ的架構思想,具有高性能、可擴展、輕量級的特性,支持不受限於機器內存大小的消息堆積能力,而且是純C#開發。同時還提供了簡單實用的管理控制台,可以讓我們輕松的管理消息隊列。比如可以方便的動態增加、禁用、啟用、刪除隊列,查詢消息內容、消息消費進度,等信息;目前,EQueue也已經經過多個迭代,並且已經有真實用戶進行使用,功能基本趨於成熟穩定。
Conference案例簡介
Conference是一個微軟開發的,基於DDD+CQRS+ES的一個開源項目。項目主頁:cqrsjourney.github.io
這個項目,對我們大家學習DDD領域驅動設計、CQRS+ES的架構,非常有幫助。
- 該案例從業務上,實現了一個典型的電子商務系統的關鍵環節:商品管理、庫存管理、下單、減庫存、支付;
- 該案例從技術上,很好的展示了CQRS架構的優點。比如可以在Q端定制不同的視圖,以應對不同的查詢需求;
- 該案例很好的向我們展示了如何划分領域,划分邊界上下文(bounded context),並如何使用不同的架構技術,實現不同的上下文,非常具有學習價值。
正是因為這個項目的以上優點,讓我有興趣使用ENode作為技術支撐,實現同樣功能的系統。在重寫的過程中,保留了所有非技術的東西,比如邊界上下文的划分、領域層等;所有技術相關的部分,用ENode替代。
使用ENode實現的Conference開源項目地址:https://github.com/tangxuehua/conference
演示地址,后台:http://www.enode.me/conference,前台:http://www.enode.me/registration
希望有興趣的同學,可以去下載源代碼進行研究。了解ENode,最快的方法就是從案例代碼開始。如果想進行交流,可以加QQ群:185916873,隨時歡迎有興趣的道友加入。
最后,貼一個Conference案例中訂單處理的Sagas流程圖:
不早了,實在寫不下去了,就到這吧。