如何理解事件溯源
在近期舉行的PHPDublin見面會上,來自DynamicRes的架構師Barry Sullivan被問到“什么是事件溯源”,作為對這個問題的回答,他在博客上寫下了這篇文章,詳細解釋了什么是事件溯源以及事件溯源有哪些好處。以下內容翻譯自Barry的博客,已獲得作者授權。
Web開發的現狀
在詳細解釋事件溯源之前,先讓我們來看看Web開發的現狀。
當前的Web開發是以數據庫作為驅動的,在設計Web應用的時候,我們會自然而然地將系統設計與數據庫存儲機制聯系在一起。如果使用的是MySQL,我們就會把數據結構設計成表,如果使用的是MongoDB,就會把數據結構設計成文檔。這樣會強制我們只關注事物的當前狀態,我們會想“怎樣保存這些數據,以便將來可以再把它們拿出來(或者修改它們)”?
這種方式存在三個問題。
1. 有悖于我們的思維
人類的思考和交流并不是以狀態為中心。如果我們在咖啡店相遇,我會問你“最近可好”?如果你只是告訴我一堆狀態卻指望我從中猜出發生了什么事情,這是非常不合情理的。
“我有一幢房子、一輛汽車、一個冰箱、三個社交媒體賬號、一只貓咪,我的右腳有點痛,我不擅長聊天,我還有另一只貓咪……”
看到沒有?這樣我會瘋掉的。你應該告訴我,從上次見面之后都發生了哪些事情,這樣我才能知道你現在的狀況是什么樣子的。簡單地說,你應該告訴我一個故事,這個故事是由一連串事件組成的。
2. 單一的數據模型
在上圖中,讀寫操作使用了相同的模型。我們從寫數據的角度來設計表,然后基于這樣的結構查詢數據。這對于小型的應用來說是沒有問題的,但用在大型的應用里就會有問題。隨著系統的增長,查詢會變得越來越復雜,總有一天,一個查詢可能會包含10個連接操作,代碼有100行那么多。系統很快就會變得脆弱無比,難以維護和變更。
3. 關鍵業務信息的丟失
這是一個大問題。在以表作為驅動的系統里,你只保存了系統的當前狀態,你根本就無法知道系統是如何達到當前狀態的。如果我問你“這個用戶修改了幾次郵件地址”,你有辦法回答嗎?或者我再問“有多少人把一件商品添加到購物車里,然后又移除掉,直到一個月之后才買了那件商品”,你就更沒法回答了。你存儲數據的方式丟掉了很多有用的業務信息!
事件溯源
事件溯源與上述的情況恰好相反,它并不關心當前狀態,而是關注持續不斷的變化事件。
舉個例子,假設我們有一個“購物車”,我們可以創建購物車,往里面添加商品或移除商品,然后結賬。
購物車的生命周期可以包含如下一系列事件:
創建購物車
往購物車里添加商品
再次往購物車里添加商品
從購物車里移除商品
結賬
這些就是一個購物車的生命周期,包含了一系列事件。這就是事件溯源,非常簡單吧?
幾乎所有的流程都可以被看成一系列事件。在與領域專家交談時,他們不會提及“表”和“連接”,他們會將流程描述成一系列事件以及可以應用在這些事件上的規則。
如何實施業務規則?
大部分的業務操作都有硬性約束。對于購物車來說,它的約束就是“一件商品必須先被放進購物車后才能被移除”。如果一件商品沒有被添加到購物車里,又怎么能夠移除它?這種事件順序是不可能發生的。在沒有狀態的情況下,你怎樣才能知道“購物車里是否有這件商品”?
很簡單,你只要檢查之前是否發生過“商品被添加到購物車里”這個事件,這樣你就可以知道購物車里是否存在這件商品,然后移除它。
這樣不會浪費時間嗎?
一點也不。一般來說,要執行約束,只需要獲得事件的一個很小子集。通過簡單的數據庫查詢就可以獲得有用的歷史事件,在加載完這些事件后重放它們,把它們“投射”出來,以此構建你的數據集。這樣的操作其實是很快的,因為你使用的是本地的處理器,而不是執行一系列SQL查詢(跨域網絡的調用要比本地操作慢得多,至少會相差兩個數量等級)。
如何展示數據?
如果說每一個狀態都是通過重放事件來獲得的,那么該如何抓取數據并把它們展示給用戶看?每次都需要抓取所有的數據然后再構建這些數據集嗎?
答案是你沒必要這樣做,這樣做其實是很荒唐的。
你可以在后臺構建數據集,然后把中間結果保存在數據庫里。這樣,用戶就可以在很短的時間內查詢到這些數據。
有了事件溯源,你就不再局限于當前的表結構。需要做其他的查詢?只要設計一個新的結構就可以了。你可以自由地實現各種讀取模型,在不需要它們的時候再把它們拋棄掉。
事件溯源的好處
1. 臨時的數據結構
因為所有的狀態都可以通過重放事件獲得,所以就沒有必要把當前“狀態”與應用程序綁在一起。如果需要以新的方式查看數據,直接創建新的數據視圖即可。不再需要繁雜的數據遷移腳本,要做的只是創建新視圖,拋棄舊視圖。我現在幾乎離不開事件溯源了。
2. 與領域專家的溝通變得更簡單
正如之前所述,領域專家通常將業務流程描述成一系列事件,而不是狀態。基于事件溯源的系統與領域專家的描述不謀而合,所以就沒有必要把他們的描述轉換成技術概念,這樣也避免了信息丟失。與領域專家的溝通因此變得更加順暢,因為我們正在使用他們能夠理解的語言與他們溝通,這也讓軟件開發變得很不一樣。
3. 極具表現力的模型
在事件溯源模型里,事件是一等對象,事件模型更加接近于實際的業務流程。這讓很多東西都變得清晰明了,你就不會陷入存儲技術的泥潭。
4. 生成報告更輕松
在事件溯源系統里,生成復雜的報告是一件輕而易舉的事情。你擁有完整的歷史事件,它們按照時間排序,你可以盡情地使用這些歷史數據。
以之前的例子為例,假設你想知道有多少個用戶從他們的購物車里移除了商品,卻在一周后購買了這些商品。按照一般的開發方式,通常需要幾周的時間才能開發出這個功能,而在發布之后,需要等上一段時間,等計算完所有數據之后才能生成報告。而在事件溯源系統里,你可以馬上得到報告。你還可以得到之前任何一個時間點的報告,仿佛擁有了一臺時光機。
5. 服務集成唾手可得
在標準的Web開發流程里,集成兩個系統通常會導致他們之間的耦合,而事件溯源系統通過事件來解耦被集成的系統。當一個系統發生某系事件需要觸發另一個系統的某個流程時,只需要寫一個事件監聽器即可。這種機制可以讓你在不修改已有領域代碼的情況下增加新的集成邏輯或特性。
例如,你想要在用戶注冊的時候發送一封歡迎郵件給他們,你只需要創建一個事件監聽器,監聽“用戶注冊”事件,而不需要去修改注冊邏輯代碼。
6. 在一般的數據庫上也能健步如飛
你不需要使用多么奇特的數據庫來存儲事件,一般的MySQL數據庫就足以。數據庫都針對追加操作進行過優化,所以存儲數據的速度是很快的。這也就是為什么事件溯源在當前的技術條件下能夠良好運作,保存事件都是追加操作。
7. 可以隨意更換數據庫
基于事件溯源的數據結構都是臨時性的,所以你可以使用你喜歡的數據庫來存儲狀態,也就是說你完全可以選擇最好的工具來完成你的工作。如果你發現了更好的工具,可以在任何時候把舊工具替換掉。我們目前正在從MySQL遷移到OrientDB,可以說是輕而易舉。
事件溯源的不足
天下沒有完美的東西,事件溯源給我們帶來了諸多好處,但也存在一些不足。
1. 最終一致性
事件溯源只能保證最終一致性。也就是說,在一個事件發生了之后,其他系統不會立即感知到它,在它們收到事件之前會有一定的延遲(比如100毫秒),所以你所投射的數據可能不是最新的。這看起來似乎是一個大問題,但其實不是的。例如,基于ReactJS構建的Web應用會基于用戶的操作事件構建狀態,查詢端出現幾毫秒的延遲并不會有什么問題。
老實講,這可以說是塞翁失馬,焉知非福。最終一致性的系統具有容錯能力,可以解決服務中斷問題。如果使用微服務架構或無服務器架構來構建分布式系統,就需要通過最終一致性來保證穩定性。
2. 事件結構發生變化
事件結構會發生變化,如果事先沒有考慮到這個問題,后續處理起來會有些麻煩。如果事件結構發生變化,需要寫一個更新器來轉換新舊事件。轉換過程可以在將數據從數據存儲中讀取出來之后進行。這個沒有它看起來那么難,只需要準備好應對策略就可以了。
3. 開發人員需要改變思維
目前的Web開發主要還是以狀態作為驅動,所以開發者習慣了從表的角度看待問題,而不是事件。我發現要讓開發者改變思維需要一些時間,因為他們需要時間來改變習慣。最好的解決辦法是讓有經驗的事件溯源開發者與傳統的開發者結對。
總結
我很喜歡事件溯源,在構建大規模分布式系統時,它幫助我們解決了很多問題。我們可以使用領域專家能夠了解的語言與他們進行溝通,我們可以自由地改變和適配系統。盡管事件溯源有一定的學習曲線,但一旦你進入到這個領域,就不會想要回頭。
原文地址:http://www.infoq.com/cn/news/2017/09/How-understand-event-traceabilit
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
- 上一篇: 新版《Windows Sysintern
- 下一篇: OpenID Connect:OAuth