成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

事件溯源(Event Sourcing)和命令查詢責(zé)任分離(CQRS)經(jīng)驗(yàn)

系統(tǒng)
CQRS 和事件源并非魔法。在開始你的旅程之前,理解這兩種模式的許多影響至關(guān)重要。

這篇文章是實(shí)現(xiàn)一個(gè)基于 CQRS 和事件溯源原則的應(yīng)用程序,描述這個(gè)過程的方式,我相信分享我面臨的挑戰(zhàn)和問題可能對(duì)一些人有用。特別是如果你正在開始自己的旅程。

一、業(yè)務(wù)背景

項(xiàng)目的背景與空中交通管理(ATM)領(lǐng)域相關(guān)。我們?yōu)橐粋€(gè) ANSP(航空導(dǎo)航服務(wù)提供商)設(shè)計(jì)了一個(gè)解決方案,負(fù)責(zé)控制特定地理區(qū)域。這個(gè)應(yīng)用程序的目標(biāo)很簡單:計(jì)算并持久化飛行數(shù)據(jù)。流程大致如下。

在飛機(jī)穿越其領(lǐng)空之前的幾個(gè)小時(shí),ANSP會(huì)收到來自 Eurocontrol 的信息,這個(gè)組織負(fù)責(zé)管理整個(gè)歐洲的航空交通。這些信息包含計(jì)劃數(shù)據(jù),如飛機(jī)類型、起飛地點(diǎn)、目的地、請(qǐng)求的航路等。一旦飛機(jī)到達(dá)了 ANSP 的 AOR(責(zé)任區(qū)域,ANSP負(fù)責(zé)控制和監(jiān)控航班的區(qū)域),我們就可以從各種來源接收輸入:航跡更新(飛行當(dāng)前位置是什么)、修改當(dāng)前航路的請(qǐng)求、由軌跡預(yù)測(cè)系統(tǒng)觸發(fā)的事件、來自沖突檢測(cè)系統(tǒng)的警報(bào)等。

雖然我們可能需要同時(shí)處理多個(gè)潛在的并發(fā)請(qǐng)求,但在吞吐量方面,與 PayPal 或 Netflix 沒法相提并論。

盡管如此,該應(yīng)用程序是安全關(guān)鍵環(huán)境的一部分。在發(fā)生關(guān)鍵故障的情況下,我們不會(huì)損失金錢或客戶,但我們可能會(huì)失去人的生命。因此,實(shí)現(xiàn)一個(gè)可靠、響應(yīng)迅速且具有彈性的系統(tǒng),以保證數(shù)據(jù)一致性和完整性,顯然是首要任務(wù)。

二、CQRS、事件溯源

這兩種模式實(shí)際上都相當(dāng)容易理解。

1.CQRS

CQRS(命令查詢責(zé)任分離)是一種將寫入(命令)和讀取(查詢)分離的方式。這意味著我們可以有一個(gè)數(shù)據(jù)庫來管理寫入部分。而讀取部分(也稱為視圖或投影)是從寫入部分派生出來的,可以由一個(gè)或多個(gè)數(shù)據(jù)庫來管理(取決于我們的用例)。大多數(shù)情況下,讀取部分是異步計(jì)算的,這意味著兩個(gè)部分并不嚴(yán)格一致。我們稍后會(huì)回到這一點(diǎn)。

CQRS 背后的想法之一是數(shù)據(jù)庫很難同時(shí)高效地處理讀寫操作。這可能取決于軟件供應(yīng)商的選擇、應(yīng)用的數(shù)據(jù)庫調(diào)優(yōu)等。例如,Apache Cassandra 在持久化數(shù)據(jù)方面效率很高,而 Elasticsearch 對(duì)搜索非常出色。使用 CQRS 真的是利用解決方案的優(yōu)勢(shì)的一種方式。

此外,我們還可以決定處理不同的數(shù)據(jù)模型。再次強(qiáng)調(diào),這取決于需求。例如,在報(bào)告視圖上使用的模型,另一個(gè)在寫入部分持久化期間效率高的非規(guī)范化模型等。

關(guān)于視圖,我們可能決定實(shí)現(xiàn)一些與消費(fèi)者無關(guān)的視圖(例如公開特定的業(yè)務(wù)對(duì)象),或者一些專門針對(duì)消費(fèi)者的視圖。

2.事件溯源

根據(jù) Martin Fowler 對(duì)事件溯源的定義:

確保應(yīng)用狀態(tài)的所有更改都存儲(chǔ)為事件序列

這意味著我們不存儲(chǔ)對(duì)象的狀態(tài)。相反,我們存儲(chǔ)影響其狀態(tài)的所有事件。然后,要檢索對(duì)象狀態(tài),我們必須讀取與該對(duì)象相關(guān)的不同事件,并逐個(gè)應(yīng)用它們。

3.CQRS + 事件溯源

這兩種模式經(jīng)常被組合在一起。在 CQRS 之上應(yīng)用事件溯源意味著將每個(gè)事件持久化在我們應(yīng)用程序的寫入部分。然后,讀取部分是從事件序列派生出來的。

在我看來,實(shí)現(xiàn) CQRS 時(shí)并不需要事件溯源。然而,反之則不一定成立。

事實(shí)上,對(duì)于大多數(shù)用例,在實(shí)現(xiàn)事件溯源時(shí)需要 CQRS,因?yàn)槲覀兛赡芟M?O(1) 的時(shí)間復(fù)雜度檢索狀態(tài),而不必計(jì)算 n 種不同的事件。一個(gè)例外是簡單的審計(jì)日志用例。在這里,我們不需要管理視圖(也不需要狀態(tài)),因?yàn)槲覀冎粚?duì)檢索日志序列感興趣。

三、領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)是一種處理與領(lǐng)域模型相關(guān)的軟件復(fù)雜性的方法。它由 Eric Evans 在 2004 年的《Domain-Driven Design: Tackling Complexity in the Heart of Software》一書中引入。

我們不會(huì)介紹所有不同的概念,但如果你對(duì)此不熟悉,我強(qiáng)烈建議你去看一看。不過,我們將只介紹在 CQRS/事件溯源應(yīng)用程序環(huán)境中有用的概念。

DDD 帶來的第一個(gè)概念是聚合(Aggregate)。聚合是一組領(lǐng)域?qū)ο螅瑥臄?shù)據(jù)變更角度來看,它們被視為一個(gè)單元。在聚合內(nèi)部的事務(wù)必須保持原子性。

與此同時(shí),聚合通過不變式來強(qiáng)制執(zhí)行自己的數(shù)據(jù)一致性和完整性。不變式就是一個(gè)規(guī)則,無論如何變化,它都必須保持為真。例如,標(biāo)準(zhǔn)終端到達(dá)航線(STAR,基本上是著陸前的預(yù)定義航線)始終與給定機(jī)場(chǎng)相關(guān)聯(lián)。一個(gè)不變式必須強(qiáng)制執(zhí)行這樣一個(gè)規(guī)則:目的機(jī)場(chǎng)不能在沒有更改 STAR 的情況下被修改,并且這個(gè) STAR 與該機(jī)場(chǎng)是有效的。

此外,作為聚合的外觀對(duì)象(處理輸入并將業(yè)務(wù)邏輯委托給子對(duì)象的對(duì)象)被稱為聚合根。

關(guān)于組成聚合的對(duì)象,我們需要區(qū)分實(shí)體和值對(duì)象。實(shí)體是具有標(biāo)識(shí)的對(duì)象,它不是由其屬性定義的。一個(gè)人的年齡會(huì)隨著時(shí)間的推移而改變,但他/她仍然是同一個(gè)人。另一方面,值對(duì)象僅由其屬性定義。不同城市的地址是不同的地址。前者是可變的,而后者是不可變的。此外,實(shí)體可以有自己的生命周期。例如,一個(gè)航班首先準(zhǔn)備起飛,然后是空中飛行,最后著陸。

在模型定義中,實(shí)體應(yīng)盡可能簡單,并專注于其標(biāo)識(shí)和其生命周期。在 CQRS/事件溯源應(yīng)用程序的上下文中,實(shí)體是一個(gè)關(guān)鍵元素,因?yàn)榇蠖鄶?shù)情況下,在聚合內(nèi)進(jìn)行的更改是基于它們的生命周期。例如,至關(guān)重要的是確保每個(gè)實(shí)體實(shí)現(xiàn)了一個(gè)函數(shù),用于確定它是否與另一個(gè)實(shí)體實(shí)例相等。這可以通過比較標(biāo)識(shí)符或一組相關(guān)屬性來實(shí)現(xiàn),從而保證了一個(gè)標(biāo)識(shí)。

既然我們已經(jīng)了解了實(shí)體的概念,讓我們回到不變式。為了定義它們,我們使用了受 BDD(行為驅(qū)動(dòng)開發(fā))格式啟發(fā)的語言:

Given [entity] at state [state]
When [event] occurs
We shall [rules]

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)是一種處理與領(lǐng)域模型相關(guān)的軟件復(fù)雜性的方法。它由 Eric Evans 在 2004 年的《Domain-Driven Design: Tackling Complexity in the Heart of Software》一書中引入。

我們不會(huì)介紹所有不同的概念,但如果你對(duì)此不熟悉,我強(qiáng)烈建議你去看一看。不過,我們將只介紹在 CQRS/事件溯源應(yīng)用程序環(huán)境中有用的概念。

DDD 帶來的第一個(gè)概念是聚合(Aggregate)。聚合是一組領(lǐng)域?qū)ο螅瑥臄?shù)據(jù)變更角度來看,它們被視為一個(gè)單元。在聚合內(nèi)部的事務(wù)必須保持原子性。

與此同時(shí),聚合通過不變式來強(qiáng)制執(zhí)行自己的數(shù)據(jù)一致性和完整性。不變式就是一個(gè)規(guī)則,無論如何變化,它都必須保持為真。例如,標(biāo)準(zhǔn)終端到達(dá)航線(STAR,基本上是著陸前的預(yù)定義航線)始終與給定機(jī)場(chǎng)相關(guān)聯(lián)。一個(gè)不變式必須強(qiáng)制執(zhí)行這樣一個(gè)規(guī)則:目的機(jī)場(chǎng)不能在沒有更改 STAR 的情況下被修改,并且這個(gè) STAR 與該機(jī)場(chǎng)是有效的。

此外,作為聚合的外觀對(duì)象(處理輸入并將業(yè)務(wù)邏輯委托給子對(duì)象的對(duì)象)被稱為聚合根。

關(guān)于組成聚合的對(duì)象,我們需要區(qū)分實(shí)體和值對(duì)象。實(shí)體是具有標(biāo)識(shí)的對(duì)象,它不是由其屬性定義的。一個(gè)人的年齡會(huì)隨著時(shí)間的推移而改變,但他/她仍然是同一個(gè)人。另一方面,值對(duì)象僅由其屬性定義。不同城市的地址是不同的地址。前者是可變的,而后者是不可變的。此外,實(shí)體可以有自己的生命周期。例如,一個(gè)航班首先準(zhǔn)備起飛,然后是空中飛行,最后著陸。

在模型定義中,實(shí)體應(yīng)盡可能簡單,并專注于其標(biāo)識(shí)和其生命周期。在 CQRS/事件溯源應(yīng)用程序的上下文中,實(shí)體是一個(gè)關(guān)鍵元素,因?yàn)榇蠖鄶?shù)情況下,在聚合內(nèi)進(jìn)行的更改是基于它們的生命周期。例如,至關(guān)重要的是確保每個(gè)實(shí)體實(shí)現(xiàn)了一個(gè)函數(shù),用于確定它是否與另一個(gè)實(shí)體實(shí)例相等。這可以通過比較標(biāo)識(shí)符或一組相關(guān)屬性來實(shí)現(xiàn),從而保證了一個(gè)標(biāo)識(shí)。

既然我們已經(jīng)了解了實(shí)體的概念,讓我們回到不變式。為了定義它們,我們使用了受 BDD(行為驅(qū)動(dòng)開發(fā))格式啟發(fā)的語言:

應(yīng)用程序設(shè)計(jì)

簡而言之,應(yīng)用程序接收命令并發(fā)布內(nèi)部事件。這些事件被持久化到事件存儲(chǔ)中,并發(fā)布給處理程序,這些處理程序負(fù)責(zé)更新視圖。我們還可以決定在視圖之上實(shí)現(xiàn)一個(gè)服務(wù)層(稱為讀處理程序)。

現(xiàn)在,讓我們?cè)敿?xì)看看不同的場(chǎng)景。

2.聚合創(chuàng)建

命令處理程序接收一個(gè) CreateFlight 命令,并在領(lǐng)域存儲(chǔ)庫中檢查實(shí)例是否存在。這個(gè)領(lǐng)域存儲(chǔ)庫管理聚合實(shí)例。它首先在緩存中進(jìn)行檢查,如果對(duì)象不存在,則會(huì)在事件存儲(chǔ)中進(jìn)行檢查。事件存儲(chǔ)是一個(gè)用于持久化事件序列的數(shù)據(jù)庫。我會(huì)稍后詳細(xì)說明我認(rèn)為一個(gè)好的事件存儲(chǔ)是什么。在這種情況下,事件存儲(chǔ)仍然為空,因此存儲(chǔ)庫不會(huì)返回任何內(nèi)容。

命令處理程序負(fù)責(zé)觸發(fā)不變式。在出現(xiàn)失敗的情況下,我們可以同步拋出異常來指示業(yè)務(wù)問題。否則,命令處理程序?qū)l(fā)布一個(gè)或多個(gè)事件到事件總線。事件的數(shù)量取決于內(nèi)部數(shù)據(jù)模型的粒度。在我們的場(chǎng)景中,我們假設(shè)發(fā)布了一個(gè)單一的 FlightCreated 事件。

在此事件上觸發(fā)的第一個(gè)組件是領(lǐng)域處理程序。這個(gè)組件負(fù)責(zé)根據(jù)實(shí)現(xiàn)的邏輯更新領(lǐng)域聚合。通常,邏輯被委托給聚合根(充當(dāng)外觀,但也可以將底層邏輯委托給子域?qū)ο螅U?qǐng)記住,聚合必須始終保持一致,并且還必須通過驗(yàn)證不變式來強(qiáng)制執(zhí)行數(shù)據(jù)完整性。

如果處理程序成功(未引發(fā)業(yè)務(wù)錯(cuò)誤),則事件將被持久化到事件存儲(chǔ)中,并且緩存將使用最新的聚合實(shí)例進(jìn)行更新。

然后,觸發(fā)視圖處理程序來更新其對(duì)應(yīng)的視圖。就像在普通的發(fā)布-訂閱模式中一樣,視圖可以只訂閱它感興趣的事件。也許在我們的情況下,視圖 2 是唯一對(duì) FlightCreated 事件感興趣的視圖。

3.聚合更新

第二種情景是更新現(xiàn)有的聚合。在接收到 UpdateFlight 命令時(shí),命令處理程序會(huì)請(qǐng)求存儲(chǔ)庫返回最新的聚合實(shí)例(如果有的話)。

如果實(shí)例已經(jīng)在緩存中,則無需與事件存儲(chǔ)交互。否則,存儲(chǔ)庫將觸發(fā)所謂的重新裝載過程。

這個(gè)過程是根據(jù)存儲(chǔ)的事件序列計(jì)算聚合實(shí)例的當(dāng)前狀態(tài)的一種方式。從事件存儲(chǔ)中檢索的每個(gè)事件(比如 FlightCreated、DepartureUpdated 和 ArrivalUpdated)都會(huì)被發(fā)布到事件總線。第一個(gè)領(lǐng)域處理程序觸發(fā) FlightCreated 時(shí)會(huì)實(shí)例化一個(gè)新的聚合(根據(jù)事件本身提供的信息,在內(nèi)存中創(chuàng)建一個(gè)新的對(duì)象實(shí)例)。然后其他領(lǐng)域處理程序(由 DepartureUpdated 和 ArrivalUpdated 事件觸發(fā))將更新剛剛創(chuàng)建的聚合實(shí)例。最終,我們能夠根據(jù)存儲(chǔ)的事件計(jì)算出狀態(tài)。

一旦計(jì)算出狀態(tài),對(duì)象實(shí)例就會(huì)被放入緩存并返回給命令處理程序。然后,其余的流程與聚合創(chuàng)建情景相同。

關(guān)于重新裝載過程還有一件事需要補(bǔ)充。如果一個(gè)聚合不在緩存中,而我們?yōu)橐粋€(gè)特定的聚合實(shí)例存儲(chǔ)了 1000 個(gè)事件,那么會(huì)花費(fèi)很長時(shí)間來計(jì)算其狀態(tài)。有一個(gè)已知的緩解措施叫做快照。

我們可以決定在每 n 個(gè)事件中持久化聚合的當(dāng)前狀態(tài)作為一個(gè)快照。這個(gè)快照也會(huì)包含在事件存儲(chǔ)中的位置。然后,重新裝載過程將簡單地從最新的快照開始,并從指定的位置繼續(xù)。快照還可以根據(jù)其他策略類型創(chuàng)建(如果重新裝載時(shí)間超過某個(gè)閾值等)。

4.如何處理事件?

我想再回顧一下我們對(duì)命令和事件的區(qū)分。首先,有必要區(qū)分內(nèi)部事件和外部事件。外部事件是由另一個(gè)應(yīng)用程序產(chǎn)生的,而內(nèi)部事件是由我們的應(yīng)用程序生成的(基于外部命令)。

我們就如何在我們的應(yīng)用程序中技術(shù)性地處理外部事件進(jìn)行了一場(chǎng)有趣的辯論。我的意思是,真正的事件指的是已經(jīng)在過去發(fā)生的事情(比如雷達(dá)軌跡)。

實(shí)際上有兩種可能的處理方法:

  • 第一種方法是將事件視為命令。這意味著我們必須首先通過一個(gè)命令處理程序,驗(yàn)證不變式,然后生成一個(gè)內(nèi)部事件。
  • 第二種方法是繞過命令處理程序,直接將事件持久化到事件存儲(chǔ)中。畢竟,如果我們談?wù)摰氖且粋€(gè)真實(shí)事件,那么驗(yàn)證不變式等操作實(shí)際上是沒有什么用的。然而,檢查事件的語法仍然很重要,以確保我們不會(huì)污染事件存儲(chǔ)。

如果我們選擇第二個(gè)選項(xiàng),可能會(huì)有興趣在聚合重新裝載期間實(shí)現(xiàn)規(guī)則。

讓我們舉一個(gè)雷達(dá)軌跡發(fā)布飛行位置的例子。如果生產(chǎn)者無法保證消息的順序,我們還可以持久化一個(gè)時(shí)間戳(由生產(chǎn)者生成),并以這種方式計(jì)算狀態(tài):

if event.date > latestEventDate {  // Compute the state
  latestEventDate = event.date} else {  // Discard the event}

這個(gè)規(guī)則將確保狀態(tài)僅基于最新生成的事件。這意味著持久化一個(gè)事件不一定會(huì)影響當(dāng)前狀態(tài)。

在第一種方法中,在持久化事件之前會(huì)實(shí)現(xiàn)這樣的規(guī)則。

5.事件模型

在事件存儲(chǔ)中持久化的事件是否需要?jiǎng)?chuàng)建一個(gè)統(tǒng)一的模型?在我看來,答案是否定的(至少大部分情況下是)。

首先,因?yàn)槲覀兛赡芟MS著時(shí)間推移持久化不同的模型版本。在這種情況下,我們必須實(shí)現(xiàn)一種策略,將一個(gè)模型版本的事件映射到另一個(gè)模型版本。

我想用一個(gè)具體的例子來說明另一個(gè)好處。假設(shè)一個(gè)應(yīng)用程序接收來自系統(tǒng) A 和系統(tǒng) B 的事件。這兩個(gè)系統(tǒng)基于各自的數(shù)據(jù)模型發(fā)布飛行事件。如果我們創(chuàng)建一個(gè)通用數(shù)據(jù)模型 C,我們需要在持久化事件之前將 A 轉(zhuǎn)換為 C 和 B 轉(zhuǎn)換為 C。然而,在項(xiàng)目的某個(gè)階段,我們只對(duì)來自 A 和 B 的某些信息感興趣。這意味著 C 只是 A 和 B 的一個(gè)子集。

但是如果以后我們需要對(duì)應(yīng)用程序進(jìn)行一些改進(jìn),并管理來自 A 和 B 的額外元素怎么辦?因?yàn)槭录鞘褂?C 格式持久化的,所以這些元素就會(huì)被簡單地丟失。另一方面,如果我們決定持久化 A 和 B 格式,我們可以簡單地對(duì)命令處理程序進(jìn)行一些改進(jìn),以管理這些元素。

四、最終一致性

1.理論

最終一致性是由 CQRS(大多數(shù)情況下)引入的一個(gè)概念。理解其影響和后果非常重要。

首先,值得一提的是有不同的一致性級(jí)別。

最終一致性是一個(gè)模型,我們可以確保數(shù)據(jù)會(huì)被復(fù)制(從 CQRS 應(yīng)用程序的寫入部分到讀取部分)。問題在于我們無法確切保證何時(shí)復(fù)制完成。這會(huì)受到各種因素的影響,比如整體吞吐量、網(wǎng)絡(luò)延遲等。這是最弱的一致性形式,但提供了最低的延遲。

在 CQRS 應(yīng)用程序中應(yīng)用最終一致性意味著在某個(gè)時(shí)刻,寫入部分可能與讀取部分不同步。

相反地,我們可以找到強(qiáng)一致性模型。除非我們?cè)诜植际较到y(tǒng)中使用相同的數(shù)據(jù)庫來管理讀取和寫入,或者我們通過使用兩階段提交向惡魔出賣了我們的靈魂,否則在分布式系統(tǒng)中我們不應(yīng)該達(dá)到這種一致性級(jí)別。

最接近的實(shí)現(xiàn)方法是,如果我們有兩個(gè)不同的數(shù)據(jù)庫,那就在單個(gè)線程中管理所有操作。這個(gè)線程將負(fù)責(zé)將數(shù)據(jù)持久化到寫入數(shù)據(jù)庫和讀取數(shù)據(jù)庫(們)。一個(gè)線程還可以專門用于單個(gè)聚合實(shí)例,并按順序處理傳入的命令。然而,如果在同步視圖時(shí)發(fā)生瞬態(tài)錯(cuò)誤,會(huì)有什么影響?我們需要補(bǔ)償其他視圖和 CQRS 應(yīng)用程序的寫入部分嗎?我們需要實(shí)現(xiàn)錯(cuò)誤重試循環(huán)嗎?我們需要通過暫停命令處理程序來停止新的傳入事件,應(yīng)用斷路器模式嗎?解決顯然會(huì)發(fā)生的瞬態(tài)錯(cuò)誤是很重要的(凡是可能出錯(cuò)的地方遲早會(huì)出錯(cuò))。

在最終一致性和強(qiáng)一致性兩種一致性模型之間,我們可以找到許多不同的模型:因果一致性、順序一致性等。舉例來說,客戶端單調(diào)一致性模型僅在會(huì)話(應(yīng)用程序或服務(wù)實(shí)例)內(nèi)保證強(qiáng)一致性。因此,實(shí)現(xiàn) CQRS 應(yīng)用程序并不只是在最終一致性和強(qiáng)一致性之間做出選擇。

我個(gè)人的觀點(diǎn)是:由于我們幾乎無法保證強(qiáng)一致性,讓我們盡可能地接受最終一致性。然而,前提是要精確理解其對(duì)系統(tǒng)其余部分的影響。

2.例子

讓我們看一個(gè)我在項(xiàng)目中遇到的具體例子。

其中一個(gè)挑戰(zhàn)是管理每架飛機(jī)的唯一標(biāo)識(shí)符。我們不得不處理來自外部系統(tǒng)(公司外部)的事件,這些系統(tǒng)中的標(biāo)識(shí)符并不相同。對(duì)于一個(gè)通道,標(biāo)識(shí)符是一個(gè)復(fù)合標(biāo)識(shí)符(出發(fā)機(jī)場(chǎng) + 出發(fā)時(shí)間 + 飛機(jī)標(biāo)識(shí)符 + 到達(dá)機(jī)場(chǎng)),而另一個(gè)通道則發(fā)送每架飛機(jī)的唯一標(biāo)識(shí)符(但第一個(gè)通道不知道)。我們的目標(biāo)是管理我們自己的唯一標(biāo)識(shí)符(稱為 GUFI,即全局唯一飛行標(biāo)識(shí)符),并確保每個(gè)事件都對(duì)應(yīng)于正確的 GUFI。

最簡單的解決方案是確保每個(gè)傳入的事件都在我們應(yīng)用程序的特定視圖中進(jìn)行查找,以關(guān)聯(lián)相應(yīng)的 GUFI。但如果這個(gè)視圖是最終一致的呢?在最壞的情況下,我們可能會(huì)有與同一飛行相關(guān)的事件,但使用不同的 GUFIs 進(jìn)行存儲(chǔ)(相信我,這是一個(gè)問題)。

一個(gè)解決方案可能是將這個(gè) GUFI 的管理委托給另一個(gè)強(qiáng)一致性的服務(wù)。

在一次問答環(huán)節(jié)中,Greg Young 提供了另一個(gè)解決方案。我們可以實(shí)現(xiàn)一種緩沖區(qū),其中只包含我們應(yīng)用程序處理的 n 個(gè)最新事件。如果視圖中不包含我們正在尋找的數(shù)據(jù),我們必須在這個(gè)緩沖區(qū)中檢查,以確保它不是剛剛在視圖之前接收到的。n 越大,減輕寫入和讀取之間的這種不一致性窗口的機(jī)會(huì)就越大。

這個(gè)緩沖區(qū)可以使用像 Hazelcast、Redis 等解決方案進(jìn)行分布式處理,也可以局部于應(yīng)用程序?qū)嵗T诤笠环N情況下,我們可能需要實(shí)現(xiàn)一個(gè)分片機(jī)制,使用哈希函數(shù)將相關(guān)對(duì)象的事件始終分發(fā)到相同的應(yīng)用程序?qū)嵗ㄗ詈檬鞘褂靡环N一致性哈希函數(shù),以便輕松擴(kuò)展)。

五、并發(fā)管理

幾個(gè)月前我已經(jīng)創(chuàng)建了一篇文章,描述了使用事件源管理并發(fā)更新的好處。

簡而言之,擁有事件存儲(chǔ)可能會(huì)幫助我們找到比悲觀或樂觀方法更聰明的解決方案來處理并發(fā)更新。

此外,在數(shù)據(jù)模型中應(yīng)用正確的粒度也是項(xiàng)目成功的關(guān)鍵。

六、選擇事件存儲(chǔ)

我們可以決定使用任何類型的數(shù)據(jù)庫來持久化事件序列。然而,最優(yōu)解往往是為事件源構(gòu)建的解決方案。

例如,隔離一個(gè)聚合實(shí)例是必須考慮的事情。假設(shè)所有事件都存儲(chǔ)在一個(gè)單一表中。這個(gè)表會(huì)隨著時(shí)間不斷增長,在聚合重建時(shí),我們將不得不過濾與一個(gè)特定聚合實(shí)例相關(guān)的事件。重建一個(gè)聚合的時(shí)間將取決于持久化的事件總數(shù),即使其中一些事件與我們感興趣的實(shí)例無關(guān)。一個(gè)好的解決方案可能是為每個(gè)聚合實(shí)例擁有一個(gè)表/存儲(chǔ)桶,以隔離事件。我們稱這個(gè)概念為流(stream)。一個(gè)流總是與一個(gè)聚合實(shí)例相關(guān)聯(lián)(在大多數(shù)用例中)。

以下是我們考慮選擇事件存儲(chǔ)時(shí)的要求:

(1) 寫入:

  • 恒定的寫入延遲:無論流的大小如何,持久化事件的延遲都必須保持恒定
  • 原子性:可以在單個(gè)事務(wù)中追加多個(gè)事件
  • TTL 管理:根據(jù)創(chuàng)建日期自動(dòng)丟棄事件
  • 無模式:可以存儲(chǔ)多種事件類型和版本

(2) 讀取:

  • 按寫入順序讀取事件
  • 從特定序列號(hào)讀取(因?yàn)榭煺眨?/li>
  • 在給定流中保持恒定的讀取性能,不受其他流的影響
  • 圖形用戶界面(GUI)
  • 緩存管理

(3) 并發(fā):

  • 樂觀并發(fā)模型
  • 冪等性管理

(4) 產(chǎn)品監(jiān)控

(5) 解決方案支持

(6) 安全性:

  • 加密(傳輸)
  • 身份驗(yàn)證
  • 授權(quán)管理

(7) 擴(kuò)展性

(8) 備份

每個(gè)上下文都是獨(dú)特的,我相信你會(huì)有自己的要求,但這至少可能是一個(gè)起點(diǎn)。

七、結(jié)論

CQRS 和事件源并非魔法。在開始你的旅程之前,理解這兩種模式的許多影響至關(guān)重要。否則,在技術(shù)和功能層面都很容易造成徹底的混亂。

然而,一旦你對(duì)約束和缺點(diǎn)有了明確的理解,CQRS 和/或事件源可能是許多問題的很好解決方案。

責(zé)任編輯:趙寧寧 來源: 技術(shù)的游戲
相關(guān)推薦

2025-03-10 00:15:00

Axon開源框架

2022-09-07 08:58:58

Node.js框架

2020-05-11 15:23:58

CQRS代碼命令

2024-09-20 08:04:54

2024-02-01 12:38:22

事件流事件溯源系統(tǒng)

2022-03-07 06:34:22

CQRS數(shù)據(jù)庫數(shù)據(jù)模型

2023-10-15 16:39:29

2023-12-13 10:44:57

事件驅(qū)動(dòng)事件溯源架構(gòu)

2011-07-04 14:50:49

QT Event 事件

2025-04-27 10:14:57

2023-02-26 10:59:51

2023-02-19 12:44:07

領(lǐng)域事件DDD

2010-06-23 11:24:23

Linux Bash命

2020-12-04 07:51:24

CQRS模型查詢

2009-12-25 17:17:45

shell命令

2021-06-02 11:28:53

查詢權(quán)責(zé)分離

2017-09-01 10:48:33

分離CQRS性能

2025-04-27 01:11:11

GolangKafkaSaga

2011-08-29 14:59:26

QtEvent事件

2018-03-28 10:32:18

微服務(wù)無服務(wù)器CQRS
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 我要看免费一级毛片 | 国产精品日韩欧美一区二区 | 久久久激情视频 | 婷婷久久综合 | 日韩一级免费大片 | 欧美一区二区成人 | 欧美电影网 | 亚洲精品视频在线播放 | 一区二区三区免费在线观看 | 有码一区| 97人人草 | 久久网站免费视频 | 热99在线| 一级做a爰片性色毛片 | 午夜精品一区二区三区在线观看 | 亚洲精品视| 91精品国产综合久久小仙女图片 | 精品国产免费一区二区三区演员表 | 国产在线精品免费 | 久久亚洲春色中文字幕久久久 | 久久伊人一区二区 | 亚洲精品一区二 | 日韩三极 | 夜夜操天天操 | 国产精品久久一区二区三区 | 中文字幕视频在线观看免费 | 亚洲三级av | 久久日韩精品一区二区三区 | 精品国产一区二区三区久久久蜜月 | 亚洲免费观看视频网站 | 色综合美女 | 国产成人免费 | 视频在线一区 | 亚洲精品在线视频 | 电影91久久久 | 一级大片 | 日韩免费视频一区二区 | 国产一区二区精品在线观看 | 国产精品一区二区在线 | 99视频在线看 | 欧美电影在线观看网站 |