事件溯源不是什麼?

banq發表於2017-05-09
事件溯源開始被使用在高事務的環境(如證券交易所或賭博公司)。今天它被用於許多其他領域。我一直和很多人討論這個架構築風格,發現對事件溯源存在很常見的誤解。

事件溯源術語是由Greg Young首先建立的,代表了一種以事件為中心的方法儲存業務實體的架構模式。一個例子如電子商務應用程式,儲存的是商品中基於屬性上執行的不斷更改的動作序列,而不是像傳統方法一樣儲存商品的最終狀態。

這使得我們能夠靈活地進行時間的移動,執行時間查詢,審計,故障排除等。它還使我們從行為方面更多地考慮到我們的應用程式,而不是靜態結構方面。

事件這個術語和持久事件日誌的存在會導致我們將事件溯源與訊息傳遞之間發生關聯,訊息傳遞是在EDA(事件驅動架構)範圍內應用的流行模式,我猜這是誤解的起點。分散式系統透過事件非同步執行所有的通訊並不意味著它是事件溯源。

我們看看下面建立key-value儲存的兩個函式,使用bash程式:

#!/bin/bash

# storage functions
db_set () {
  [[ $(grep "^$1," state_store) ]] && sed -i '' -e "s/^$1,.*/$1,$2/" state_store || echo $1,$2 >> state_store
}

db_get () {
  grep "^$1," state_store | sed -e "s/^$1,//"
}

# main flow
db_set name luise
db_set age 32
db_set name anna
db_get name
db_get age
<p class="indent">

這個指令碼包含兩個函式db_set和db_get,負責向檔案儲存或獲得一對key/value值,db_get是根據指定key獲得相應的值,db_set是在沒有發現指定key則新增一對key/value其中,如果已經存在key,則修改對應key的value。

如果我們執行上述指令碼,state_store檔案將是:
name,anna
age,32

執行這個指令碼輸出如下:
name,anna
age,32

這個key-value中儲存的是最新的key/value鍵值對,這種僅僅儲存最新狀態的方式有一些缺點,比如無法對如何導致當前狀態的原因無法進行審計或故障排除。

讓我們改一下上面的例子,我們可以這樣做:

#!/bin/bash

# storage functions
db_set () {
  echo "$1,$2" >> event_store
}

db_get () {
  grep "^$1," event_store | sed -e "s/^$1,//" | tail -n 1
}

# main flow
db_set name luise
db_set age 32
db_set name anna
db_get name
db_get age

<p class="indent">

上述示例是附加了一個非常簡單的不可變事實/事件,表示“此條目已用此值更新”。

db_set函式現在只需將key/value鍵值對追加到event_store檔案檔案的末尾,而不是嘗試更改現有狀態。這樣event_store檔案儲存的是所有更改動作集合列表。那麼從這個event_store檔案讀取函式db_get就有點複雜了,因為我們希望僅顯示返回最後一個狀態,而不是所有更改動作列表。

現在event_store檔案將是如下資料:
name,luise
age,32
name,anna

而執行上面新指令碼的輸出仍將與前面的示例相同。

這是一個太簡單的案例,而且絕對不適合生產領域。示例中的事件模式是一對簡單的鍵/值,允許我們擁有PUT語義。在現實情況下,我們還需要跟蹤事件時間和事件模式版本。

但是這個案例仍然顯示了事件溯源的有趣屬性:

1.可審計性:檢查過去的事件
2.高效能寫入:向儲存器追加是最易於寫操作的。
3.可重放性:可以來回播放我的事件順序
4.時間查詢:是的,我們只需要新增該時間戳:)

現在我們總結一下事件溯源不是什麼:

它不是頂級架構

頂級架構是指如何部署多個元件並相互通訊。例如,這個架構可能是EDA(事件驅動架構)或SOA(面向服務的架構)。事件溯源則不是這樣的。相反,事件溯源是一種應用程式架構模式,並且與任何其他設計模式一樣,它透過一種良好的記錄方法來解決特定的常見問題。

將事件溯源應用於整個系統實際上被認為是反模式。這是一種在內部建立事件溯源的大型單體monolith方法。


它不是框架
我們不構建事件溯源框架,我們不應為任何其他設計模式構建框架。

儘管如此,事件溯源意味著永續性,事件儲存可以是一個有助於應用該模式的隔離系統,但它不是模式本身。常用儲存系統的示例是Event Store和Apache Kafka。

它不是CQRS
事件溯源和CQRS(命令查詢責任分離)已經被應用在一起幾年了,但它們是不同的模式。

Bertrand Meyer在1988年的“物件導向軟體構造”一書中,CQRS最初被記錄為CQS(命令查詢分離),並且從那時起已被應用了很多。

將狀態建模為事件,很自然需要不同的讀取方式,因為不能直接讀取狀態了,必須把事件轉為狀態。客戶不想收到一連串的操作事件。相反,他們希望對他們期望的資訊保持一致的狀態。針對某些型別的查詢或跨越多個有界的上下文,我們可以實現另外一種適合讀取的儲存方式(快取記憶體,物化檢視等)。

它不是非同步或最終一致
它與最終的一致性無關。前面上述指令碼正在儲存事件,每個讀取可以正在抓取所有事件以顯示最新狀態。

我們也可以非同步地做這些,我可以透過後臺任務讀取事件和更新其他地方的狀態,並改變讀取路徑。這將使其最終保持一致。但是我們不必這樣做。

世界上最流行的事件溯源應用是Git。Git是完全同步的,並且具有作為事件溯源的所有優點,例如在時間上進行時間查詢或及時來回地在不同的時間點中挑選程式碼以及其他用例。

它不儲存發生的一切

除非事件非常少量或有足夠的儲存空間,否則狀態並不是由每一個曾經存在的事件構成的。與事件溯源相關的是執行快照或壓縮的概念,從什麼時候開始播放事件變得很重要,因此重視“時間的開始”更為現實。

從此得出結論?

事件溯源通常被錯誤地與EDA(事件驅動架構)相關聯。事件溯源是一種應用程式架構模式,它允許更好的審計,提供狀態重播或遷移動到特定時間點的能力,它自然地提高了寫方面的效能改進,因為將事件追加(不是修改)到持久儲存中真的很快。

採集整個系統的事件是一個很大的錯誤,被認為是一種反模式。它本身不是架構,它不會使應用程式更好地通訊,它只是透過儲存發生的事實而不是儲存當前檢視,使單個應用程式更加一致和可審計。

這很像當我丟失了車鑰匙,只能在我的頭腦中重播所有剛發生的事情,並且我同步地和一致地做這些事情,直到我再次找到它,否則我需要乘坐公共汽車。


What Event Sourcing is not

相關文章