CQRS本身是一個(gè)非常簡(jiǎn)單的模式。它只規(guī)定了處理命令的應(yīng)用程序的組件應(yīng)該與處理查詢(xún) 的組件分離谐岁。 雖然這種分離本身非常簡(jiǎn)單,但它與其他模式結(jié)合時(shí)提供了許多非常強(qiáng)大的功能。Axon 提供的構(gòu)件更容易實(shí)現(xiàn)不同的模式與CQRS的結(jié)合伊佃。
下圖顯示了一個(gè)示例窜司,一個(gè)基于CQRS的事件驅(qū)動(dòng)的架構(gòu)圖示。 左側(cè)顯示的是UI組件, 通過(guò) 兩種方式與應(yīng)用程序的其余部分進(jìn)行交互:它向應(yīng)用程序發(fā)送命令 (顯示在頂端的部分), 并從應(yīng)用程序中查詢(xún)信息 (顯示在底端的部分)锭魔。
Command(命令)通常用簡(jiǎn)單的對(duì)象表示例证,這些對(duì)象包含命令處理器執(zhí)行所需的所有數(shù)據(jù)。一個(gè)命令通過(guò)它的名字來(lái)表達(dá)它的意圖迷捧。在Java術(shù)語(yǔ)中织咧,這意味著使用類(lèi)名來(lái)確定需要做什么,命令的字段提供了執(zhí)行該操作所需的信息漠秋。
Command Bus接受命令并路由它們到命令處理器(Command handler) 笙蒙。每個(gè)命令處理器響應(yīng)特定類(lèi)型的命令,并根據(jù)命令的內(nèi)容執(zhí)行邏輯庆锦。 然而捅位,在某些情況下,你也希望不顧實(shí)際的命令類(lèi)型去執(zhí)行邏輯搂抒,如驗(yàn)證艇搀、日志或權(quán)限。
Command handler(命令處理器)從倉(cāng)儲(chǔ)中恢復(fù)領(lǐng)域?qū)ο?聚合)并執(zhí)行方法來(lái)改變它們的狀態(tài)求晶。這些聚合通常包含真實(shí)的業(yè)務(wù)邏輯并負(fù)責(zé)維護(hù)自身的不變性焰雕。聚合的狀態(tài)變化導(dǎo)致了領(lǐng)域事件的產(chǎn)生,領(lǐng)域事件和聚合都從領(lǐng)域模型中來(lái)。
repositories(倉(cāng)儲(chǔ))負(fù)責(zé)提供訪問(wèn)聚合芳杏。通常情況下矩屁,這些倉(cāng)儲(chǔ)的優(yōu)化設(shè)計(jì)是僅通過(guò)其唯一標(biāo)識(shí)符來(lái)查找聚合。一些倉(cāng)儲(chǔ)將存儲(chǔ)聚合自身的狀態(tài)(例如爵赵,使用對(duì)象關(guān)系映射吝秕,ORM), 而另一些則存儲(chǔ)聚合的狀態(tài)的更改到Event Store中空幻,倉(cāng)儲(chǔ)還負(fù)責(zé)對(duì)其備份數(shù)據(jù)庫(kù)中的聚合進(jìn)行更改烁峭。
Axon提供了直接持久化聚合(使用對(duì)象關(guān)系映射,ORM)和事件溯源(event sourcing)兩種方式.
Event bus(事件總線)分派事件到所有感興趣的事件監(jiān)聽(tīng)器(Event listener)中秕铛≡蛱辏可以同步或異 步完成。異步事件調(diào)度允許命令執(zhí)行返回和移交控制給用戶(hù)如捅,這些事件在后臺(tái)被分派和處理,不必等待事件處理完成调煎,這樣應(yīng)用程序就會(huì)響應(yīng)得更快镜遣。同步事件處理,另一方面,更簡(jiǎn)單悲关,是 一個(gè)合適的默認(rèn)選項(xiàng)谎僻。 默認(rèn)情況下,同步處理將在同一事務(wù)中處理事件監(jiān)聽(tīng)器和命令處理 器。
Event listener(事件監(jiān)聽(tīng)器)接收并處理事件寓辱。一些處理器將更新用于查詢(xún)的數(shù)據(jù)源艘绍,而其他處理器則將消息發(fā)送到外部系統(tǒng)。你可能會(huì)注意到了,命令處理器只對(duì)組件所做的更改感興趣秫筏,卻完全察覺(jué)不到它們的存在诱鞠。這意味著它可以非侵入地?cái)U(kuò)展應(yīng)用程序的新功能, 你需要做的就只是添加一個(gè)事件監(jiān)聽(tīng)器这敬。事件讓?xiě)?yīng)用程序中的所有組件松散耦合航夺。
在某些情況下,事件處理需要把新的命令發(fā)送到應(yīng)用程序。這方面例子崔涂,例如阳掐,當(dāng)接收到一 個(gè)訂單,這可能意味著客戶(hù)的賬戶(hù)應(yīng)當(dāng)扣除購(gòu)買(mǎi)商品所需的金額冷蚂,并必須通知裝運(yùn)方準(zhǔn)備打包這批商品缭保。在許多應(yīng)用中,邏輯將變得更加復(fù)雜:如果客戶(hù)沒(méi)有及時(shí)支付呢?你會(huì)馬上發(fā)貨還是等待付款蝙茶?saga(長(zhǎng)事務(wù)處理)是CQRS的概念艺骂,它負(fù)責(zé)管理這些復(fù)雜的商業(yè)事務(wù)。
在用戶(hù)界面和數(shù)據(jù)源之間的瘦數(shù)據(jù)層尸闸,為實(shí)際的查詢(xún)實(shí)現(xiàn)提供了一個(gè)明確定義的接口彻亲。這個(gè)數(shù)據(jù)層通常返回包含查詢(xún)結(jié)果的只讀DTO對(duì)象。這些DTO的內(nèi)容通常是由用戶(hù)界面的需求驅(qū) 動(dòng)的吮廉。在大多數(shù)情況下,他們直接映射到用戶(hù)界面的一個(gè)特定的視圖(也稱(chēng)為table-per-view)苞尝。
Axon不提供任何應(yīng)用程序這部分的構(gòu)件。主要的原因是這非常簡(jiǎn)單宦芦,并且與分層架構(gòu)并沒(méi)有太大的區(qū)別宙址。
Axon模塊結(jié)構(gòu)
Axon Framework由多個(gè)針對(duì)CQRS特定問(wèn)題域的模塊組成。根據(jù)項(xiàng)目的具體需求调卑,你需要包括一個(gè)或多個(gè)這些模塊抡砂。
從Axon2.1起,所有模塊兼容OSGi。這意味著它們?cè)趍anifest文件包含所需的頭文件,并聲明它們導(dǎo)入和導(dǎo)出的包恬涧。目前,只有Slf4J bundle(1.7.0 <= 版本< 2.0.0)是必需的注益。所有其他導(dǎo)入的都被標(biāo)記為可選,盡管你很可能需要這些。
主模塊
Axon的主要模塊已經(jīng)過(guò)完全的測(cè)試溯捆,強(qiáng)大到足以在苛刻的生產(chǎn)環(huán)境中使用丑搔。所有這些模 塊的maven groupId都是org.axonframework。
核心模塊顧名思義是Axon的核心組件。如果你使用single-node設(shè)置,這個(gè)模塊可能會(huì)提供給你需要的所有組件啤月。其他所有的Axon模塊依賴(lài)這個(gè)模塊煮仇,所以它必須在classpath中可用。
測(cè)試模塊包含有你可以使用的測(cè)試固件來(lái)測(cè)試基于Axon的組件谎仲,比如你的命令處理器浙垫、聚 合和Sagas。你通常在運(yùn)行時(shí)不需要這個(gè)模塊郑诺,只需在測(cè)試過(guò)程中添加到classpath夹姥。
分布式CommandBus包含了可用于在多個(gè)節(jié)點(diǎn)發(fā)送命令的實(shí)現(xiàn)。它配備了JGroups和Spring Cloud連接器间景,來(lái)連接這些節(jié)點(diǎn)佃声。
AMQP模塊提供的組件,允許你打造一個(gè)基于AMQP的EventBus倘要,并使用message broker作為分配機(jī)制圾亏。這允許保證事件送達(dá),即使事件處理器節(jié)點(diǎn)暫時(shí)不可用封拧。
Spring模塊允許Axon組件在Spring程序上下文中配置志鹃。它還提供了一些針對(duì)Spring框架的構(gòu)件的實(shí)現(xiàn),例如用于在Spring消息信道上發(fā)布和檢索Axon事件的適配器泽西。
MongoDB是一個(gè)基于文檔的NoSQL數(shù)據(jù)庫(kù)曹铃,Mongo模塊實(shí)現(xiàn)事件和saga的存儲(chǔ),并用MongoDB數(shù)據(jù)庫(kù)存儲(chǔ)事件流和saga捧杉。
幾個(gè)AxonFramework組件提供監(jiān)控信息陕见。Metrics模塊提供基于Codehale的基本實(shí)現(xiàn)來(lái)收集監(jiān)控信息。
與Axon API一起工作
QRS是一種架構(gòu)模式味抖,它不可能提供一個(gè)適合所有項(xiàng)目的單一解決方案评甜。顯然,Axon Framework并不試圖提供這樣的解決方案仔涩。相反忍坷,Axon提供了遵循最佳實(shí)踐的實(shí)現(xiàn)和將每一個(gè)實(shí)現(xiàn)都調(diào)整到符合你的特定需求的手段。
幾乎所有基礎(chǔ)設(shè)施構(gòu)件都提供檢查點(diǎn)(如攔截器,解析器等)熔脂,允許你向這些構(gòu)件添 加application-specific行為佩研。大多數(shù)情況下,Axon將為那些適合大多數(shù)用例的檢查點(diǎn)提供實(shí)現(xiàn)霞揉,如果需要旬薯,你可以簡(jiǎn)單地實(shí)現(xiàn)自己的。
非基礎(chǔ)設(shè)施對(duì)象适秩,如消息袍暴,通常是不可變的些侍。這確保了這些對(duì)象在多線程環(huán)境中是安全的, 沒(méi)有副作用的政模。
為了確保定制的最大化,所有Axon組件都使用接口定義蚂会。抽象類(lèi)和具體的實(shí)現(xiàn)類(lèi)提供了幫助你的方式淋样,但框架對(duì)你沒(méi)有任何要求,它總是允許使用該接口構(gòu)建任何完全自定義實(shí)現(xiàn)的構(gòu)件胁住。
Spring的支持
Axon Framework對(duì)Spring提供了廣泛的支持趁猴,但不強(qiáng)制你使用Spring來(lái)使用Axon。所有的組件都可以以編程方式配置彪见,并且不需要把Spring放在classpath中儡司。然而,如果你使用Spring余指、使 用Spring的annotation捕犬,很多的配置會(huì)變得更加容易。