淺談「領(lǐng)域事件」實(shí)踐

領(lǐng)域事件是DDD,Event Sourcing等架構(gòu)中的一個(gè)重要概念。
基于工作環(huán)境中的一些實(shí)踐經(jīng)驗(yàn),我總結(jié)了一些粗淺的體會(huì)。
文中不會(huì)有非常高深復(fù)雜的定義,只使用一些基本的概念。

領(lǐng)域事件定義

國(guó)外書籍中很少使用下定義的方式解釋一個(gè)名詞的含義。一般都會(huì)使用作詮釋的方式:“領(lǐng)域事件可以怎樣怎樣”、“領(lǐng)域事件會(huì)帶來什么什么”。

讓我來下定義的話:領(lǐng)域事件是微服務(wù)系統(tǒng)中各服務(wù)通信的一種方式。和RPC類似,領(lǐng)域事件用于在服務(wù)之間交換信息,但領(lǐng)域事件并不追求實(shí)時(shí)一致性,僅要求數(shù)據(jù)的最終一致性。

各服務(wù)在一些關(guān)鍵有意義的業(yè)務(wù)節(jié)點(diǎn)完成后會(huì)發(fā)送領(lǐng)域事件,代表一些關(guān)鍵的動(dòng)作「已完成」。因此領(lǐng)域事件的命名多采用過去分詞,例如「訂單已發(fā)送Order sent」「訂單已取消Order canceled」。

領(lǐng)域事件有多種實(shí)現(xiàn)方式。我工作中使用到的方式是:將一些關(guān)鍵的、其他服務(wù)可能用到的信息,打包成一個(gè)結(jié)構(gòu)體,以消息的形式,通過消息隊(duì)列中間件,異步傳輸給系統(tǒng)中訂閱該消息的服務(wù)。

領(lǐng)域事件帶來的收益

介紹領(lǐng)域事件的書籍從不吝惜對(duì)領(lǐng)域事件優(yōu)點(diǎn)的贊美。在此我針對(duì)各優(yōu)點(diǎn)談?wù)勛约旱母惺堋?/p>

增強(qiáng)服務(wù)自治性

這大概是領(lǐng)域事件最常被提及的優(yōu)點(diǎn)了。何謂“自治性”?我的理解是:一個(gè)服務(wù)只需要關(guān)注自己,不與其他服務(wù)耦合的程度。單體服務(wù)不依賴其他任何服務(wù),自治性最高;如果一個(gè)服務(wù)調(diào)用了大量其他服務(wù)的RPC,那么它的穩(wěn)定性和業(yè)務(wù)邏輯必定受其他服務(wù)的影響,自治性稍差。

為什么能提升自治性?個(gè)人認(rèn)為有以下原因:

首先領(lǐng)域事件一般使用消息隊(duì)列實(shí)現(xiàn),消息隊(duì)列是異步的,不會(huì)阻塞調(diào)用。如果因一時(shí)網(wǎng)絡(luò)原因消息無法傳達(dá),那么也會(huì)由消息組件重試。領(lǐng)域事件可以看做異步的、非阻塞的、由第三方保證最終送達(dá)的RPC,可靠性比普通的RPC更高。

另一點(diǎn)就是領(lǐng)域事件不必指定消息的接收方是誰。假設(shè)我們有這樣的業(yè)務(wù)場(chǎng)景,A服務(wù)完成了某一動(dòng)作后,需要通知B。如果我們使用RPC通信,后面業(yè)務(wù)擴(kuò)展時(shí)增加了C,D服務(wù)需要通知,則實(shí)現(xiàn)時(shí)我們需要修改A的代碼,A的業(yè)務(wù)邏輯可能還要受到C,D處理結(jié)果的影響。但是如果我們使用領(lǐng)域事件進(jìn)行通信,則在新增C,D服務(wù)之后,讓它們訂閱A發(fā)出的事件即可,A服務(wù)完全不需要修改,不受其他服務(wù)的影響,提升了自治性。

回放系統(tǒng)動(dòng)作

如果我們把系統(tǒng)看做一個(gè)狀態(tài)機(jī),則領(lǐng)域事件可以看做推動(dòng)狀態(tài)機(jī)轉(zhuǎn)移狀態(tài)的輸入。領(lǐng)域事件是具有業(yè)務(wù)含義的,回放某一時(shí)間段的領(lǐng)域事件可以復(fù)現(xiàn)用戶操作。通過數(shù)據(jù)庫(kù)binlog回放也可以達(dá)到類似的效果,但binlog是沒有業(yè)務(wù)語義的,回放結(jié)果沒有準(zhǔn)確的解讀方式。

回放領(lǐng)域事件當(dāng)然需要某種消息存儲(chǔ)/檢索機(jī)制,這里不做展開。

實(shí)踐領(lǐng)域事件時(shí)的一些思考

  • 領(lǐng)域事件實(shí)時(shí)性
    領(lǐng)域事件可以保證最終一致性,但不能保證數(shù)據(jù)實(shí)時(shí)性。產(chǎn)生的時(shí)延是否是業(yè)務(wù)可以接受的?《實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)精粹》中進(jìn)行了一個(gè)精妙的類比:在軟件出現(xiàn)前,傳統(tǒng)業(yè)務(wù)的執(zhí)行流程是怎樣的?流程步驟之間是否會(huì)有一定的時(shí)間差?如果領(lǐng)域事件導(dǎo)致的時(shí)延不超過無軟件時(shí)業(yè)務(wù)流程的時(shí)延,那么這時(shí)延就是可以接受的。當(dāng)然,實(shí)際生產(chǎn)環(huán)境中,說服用戶接受這樣的時(shí)延通常不是件容易的事情。

  • 領(lǐng)域事件定義
    領(lǐng)域事件有業(yè)務(wù)語義,這個(gè)業(yè)務(wù)語義所代表的動(dòng)作,必須要有明確的定義。例如:在一個(gè)招聘系統(tǒng)中,目前只有“現(xiàn)場(chǎng)面試簽到”這一個(gè)場(chǎng)景。那么這個(gè)動(dòng)作完成以后,應(yīng)該發(fā)出「面試已簽到」領(lǐng)域事件,還是發(fā)出「現(xiàn)場(chǎng)面試已簽到」的領(lǐng)域事件呢?如果是前者,那么后續(xù)新增的其他類型面試簽到,也要發(fā)出這個(gè)領(lǐng)域事件。領(lǐng)域事件的業(yè)務(wù)語義必須得到準(zhǔn)確的解釋,從命名開始。領(lǐng)域事件表示的動(dòng)作粒度也要在定義前就想好。

  • 領(lǐng)域事件管理
    目前我們有yAPI這樣優(yōu)秀的接口管理工具,但還沒有合適的領(lǐng)域事件管理工具。領(lǐng)域事件和接口都是用于通信的手段,所以他們的含義,包括功能、每個(gè)字段含義等,必須得到準(zhǔn)確的解釋。否則當(dāng)領(lǐng)域事件增加到一定規(guī)模,有人員更迭時(shí),了解各個(gè)領(lǐng)域事件將成為惱火的事情。

  • 向領(lǐng)域事件改造
    EDA是一種時(shí)髦的架構(gòu),但并非所有服務(wù)一開始就會(huì)選用這種架構(gòu)。一個(gè)單純由RPC通信的系統(tǒng),想要轉(zhuǎn)型到通過領(lǐng)域事件通信會(huì)遇到很多問題。我們系統(tǒng)遇到的一個(gè)現(xiàn)實(shí)問題是:太多的接口可能會(huì)發(fā)送同一種領(lǐng)域事件,如果在所有可能的代碼后加上領(lǐng)域事件,很可能造成遺漏。我們系統(tǒng)對(duì)此的解決方案是:將領(lǐng)域事件發(fā)送收斂到數(shù)據(jù)庫(kù)上。業(yè)務(wù)操作,最終的效果是修改數(shù)據(jù)庫(kù),那么我們就監(jiān)聽相應(yīng)數(shù)據(jù)庫(kù)的binglog,發(fā)出相應(yīng)的領(lǐng)域事件。
    這種解決方案可能會(huì)有以下問題:
    首先一個(gè)領(lǐng)域事件可能對(duì)應(yīng)不只一張表的修改。為了不遺漏領(lǐng)域事件,我們不得不監(jiān)聽多張表。每個(gè)表修改時(shí),我們都會(huì)發(fā)出領(lǐng)域事件,這就導(dǎo)致業(yè)務(wù)上同一個(gè)領(lǐng)域事件可能會(huì)重復(fù)發(fā)送。冪等操作只能交給接收方來做了。
    監(jiān)聽binlog發(fā)送領(lǐng)域事件帶來的另一個(gè)改造問題是消息中各字段的準(zhǔn)確性問題。正常發(fā)送領(lǐng)域事件時(shí),在業(yè)務(wù)處理的過程中就能獲取領(lǐng)域事件的各個(gè)字段。但一張表上可能不會(huì)有完整的領(lǐng)域事件信息,需要反查其他表進(jìn)行字段填充。某一業(yè)務(wù)操作會(huì)修改t1,t2兩張表,A,B兩個(gè)業(yè)務(wù)操作前后發(fā)生。A操作導(dǎo)致的t1 binlog消息到達(dá)了,查詢t2表上的字段,此時(shí)t2卻已經(jīng)被B操作修改了,則反查出來的結(jié)果是B操作后的數(shù)據(jù)結(jié)果。那么拼裝消息時(shí),部分字段是A導(dǎo)致的,部分字段是B導(dǎo)致的,就會(huì)組裝出一個(gè)沒有真實(shí)發(fā)生的領(lǐng)域事件,可能對(duì)接收方有影響,具體影響還需要接收方想辦法化解。

  • 領(lǐng)域事件處理
    處理消息逃不開的三個(gè)問題:冪等,重試,丟失。真實(shí)寫代碼時(shí)有太多細(xì)節(jié)需要注意,只能case by case的看了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容