天天看點

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

有位同學給我發了一頁張逸的書,讓我評點一下其中觀點。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖1 摘自《解構領域驅動設計》(張逸,2021)

圖檔中的“狀态和事件本質上是相同的”真是令我“耳目一新”。那就針對這頁書的内容來講講吧。

我先講一講狀态機的一些知識點,然後根據知識點來評價一下張逸這頁書的内容。

一、狀态是描述某個類的“形容詞”

狀态的名稱和類的名稱湊到一起,“狀态的類”或“這個類是狀态的”應該能說得通。

(也可以把整個系統當成一個類來描述狀态,這時得到的狀态機相當于系統的需求規約,這樣的狀态機往往是非常龐大的。)

例如,針對“人”這個類,描述它的形容詞可以有:高、矮、胖、瘦、貧、富、美、醜……等。“高的人”、“美的人”、“這個人是高的”、“這個人是美的”是可以說得通的,這些都可以作為“人”的狀态。

有的“形容詞”是動詞變化而來的,例如,“健身”是動詞,但“正在健身的”、“已健身的”就變成了形容詞,“正在健身的人”可以說得通。

我們看英文書籍中的狀态機圖,往往可以看到很多名稱中帶有“ing”、“ed”的狀态,就是現在分詞、過去分詞作為形容詞使用。“domain-driven”就屬于這種情況,說domain-driven(定語) design或說this design is domain-driven(表語)是可以的。

圖2是狀态圖。節點是狀态,形容詞;邊是事件/動作,動詞。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖2 狀态圖是這樣的

光是這一點,不少網絡上的“狀态圖”(statechart)、“狀态機圖”(state machine diagram)就已經趴下了。有的文章說“***是一張狀态(機)圖”,結果一看所給出的圖上的節點,動詞!這分明是活動圖(或流程圖、資料流圖)嘛。

例如,下面這張圖3,左上角說是描述複合狀态(Compound States),但節點卻是動詞,這是錯誤的。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖3 某繪圖工具給出的示例(摘自網絡)

節點是動詞,那是活動圖,如圖4。活動圖的邊隐含着對象(或資料)流,名詞。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖4 活動圖是這樣的

根據以上知識點,我們來看一下張逸書頁中的觀點。圖5是圖1的一部分,我特地圈出了要評論的内容。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖5 圖1的一部分,加了标注

圈出的地方,張逸的陳述如下:

①狀态和事件本質是相同的,雖然UML狀态圖沒有把狀态視為事件。

②狀态就是領域事件。

③領域事件的命名是動詞的過去時态。

我的評論

張逸的陳述①相當于認為狀态機的數學模型(如圖6)中的Σ和S是一個東西,這個“創新”要是成立,整個理論體系都要推翻重來了。至于為什麼張逸會有這樣的認識,後面的段落還會繼續深挖其中的可能原因。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖6 有限狀态機的數學模型,摘自wikipedia

張逸的陳述①說的事件是UML狀态機中的事件,并沒有直說這個事件就是所謂的“領域事件”。也許“領域事件”和“事件”還不一樣,陳述②說的“狀态就是領域事件”沒準就是對的呢?

說鹿是馬不合适,要是我定義我這個鹿是“領域鹿”,然後說它其實就是馬,也不是不可以,對吧?

那我們來看看這個“領域鹿”,不,“領域事件”是什麼,然後就從本小節提到的知識點——詞性來說一說張逸的陳述②和③。

“Domain Event”這個詞不是DDD圈子首先用的,例如圖7所示的這篇1999年的文章,就使用了domain events的說法。文中的information base用面向對象的術語來說就是“類和關系”。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖7 摘自Action Inventory for a Knowledge-Based Colloquium Agent(Erik Sandewall, 1999)

當然,DDD圈子可以自行定義這個詞。以下是Martin Fowler的定義:

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖8 摘自https://martinfowler.com/eaaDev/DomainEvent.html

從Fowler的陳述和所給類圖可以知道,領域事件實際上就是一個“行為記錄”類,像錄像機一樣,把發生過的事情的一些細節記下來。就是這麼一個東西,沒有必要過度渲染,活生生搞成玄學。

Fowler加了一個限定“affects the domain”,也就是說,不是什麼都記,影響領域的才記。“影響領域”是一個模糊的說法,後面Fowler又補充得更精确一些:“can trigger a change to the state of the application(可以觸發應用的狀态變化)”。

以下是Greg Young的說法:

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖9 摘自http://codebetter.com/gregyoung/2010/04/11/what-is-a-domain-event/

從Greg Young的說法可以得知,DDD話語中,領域事件的命名是動詞的過去式,張逸的陳述③是沒錯的。就拿“domain-driven”來說,領域事件命名應該是“domain-drove”。

但是,動詞的過去式還是動詞,說的是瞬間的行為,不是形容詞,不能用來做定語或表語,不能作為狀态的名稱。

do的過去式是did,不能作為形容詞,可以作為形容詞使用的是“to do”、“doing”、“done”,這也是我們常見到的狀态的名稱。

張逸可能混淆了“過去式”和“過去分詞”(完成态),混淆了行為和狀态。did是一個行為,瞬間發生就結束,done是一個狀态,系統可以停留在那裡很久。

規則動詞的過去式和過去分詞後面都是ed,也許正是這一點讓張逸認為這兩個ed是一回事,進而得出結論“狀态就是領域事件”。碰到不規則動詞,這個問題就暴露出來了。

如果把領域事件了解成“行為記錄”而不是“行為”,那麼這個動詞其實是名詞。例如,“我的奮鬥”、“嫌疑人X的獻身”以及“領域驅動設計”就是動詞的名詞化。

Fowler和Young都沒有說“狀态就是領域事件”。Fowler隻是說領域事件觸發狀态的變化。

事件風暴(我重點批評的僞創新之一)的“發明”者,Alberto Brandolini在他的書中,也說:

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖10 摘自 Introducing EventStorming(Alberto Brandolini,2018)

從Brandolini的陳述我們知道,他也認為領域事件用動詞的過去式命名,另外他還提到“Domain Events as state transitions(領域事件作為狀态遷移)”,這個說法和Fowler類似。

由此我們得知:

(1)DDD話語裡面的Domain Event的命名确實是過去式。

(2)所列這幾位沒有說Domain Event相當于狀态,最多說了相當于狀态遷移。

(3)“狀态和事件本質是相同的”,“狀态就是領域事件”的說法可能是張逸自己加上去的。

張逸當然有資格發展出自己的東西,但最好在了解已有知識的基礎上再發展,否則容易陷入“僞創新”。

張逸為什麼要這樣說呢?表面上的原因似乎是上面說的:

(1)他混淆了過去式和過去分詞。

(2)他混淆了狀态和遷移。

但問題并沒有那麼簡單。

假如張逸退一步,不說“狀态和事件本質是相同的”,“狀态就是領域事件”,改口說“領域事件和狀态是一一對應的,把事件的名稱變換個形式就是狀态了”,例如“did→done”,“broke→broken”,那可以嗎?

依然是不對的!這也許就是導緻張逸認為“狀态和事件本質是相同的”的本質原因。

因為

二、狀态和事件不是一一對應的

雖然現在分詞、過去分詞這樣的“形容詞”可以作為狀态的名稱,但并非狀态的優選名稱。

就拿人的例子來說,一個人發生了“健身”的行為,他可能有什麼狀态變化?

可能有的人會像圖11那樣,說狀态為“未健身”、“已健身”:

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖11 不合适的狀态

“未健身”、“已健身”作為狀态并非不可以,但外部的對象可能并不在意這個人是否健身,在意的可能是“美”和“醜”,如圖12。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖12 更合适的狀态

而要從醜到美,還有其他的遷移路線,如圖13,多個事件可以觸發到同一狀态的遷移。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖13 事件和狀态不是一一對應

或者看“技術”一點的例子,棧(Stack)。事件是壓入(Push)和彈出(Pop),但我們談論棧的狀态時,顯然不是像圖14那樣:

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖14 不合适的棧狀态

更合适的棧狀态如圖15:

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖15 合适的棧狀态

為突出重點,以上狀态機圖隻保留了遷移的事件,忽略了警戒條件、動作等内容。

從圖15可以看出,要遷移到“半滿”狀态,觸發的事件可以是“壓入”,也可以是“彈出”;而“壓入”事件,可能會導緻遷移到“半滿”,也可能會導緻遷移到“滿”。狀态和事件不是一一對應的。

再看圖16的交通燈例子,狀态三個,事件就一個Timer_Tick。啥,“轉黃”、“轉綠”等行為在哪裡?藏在各個狀态的入口動作中。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖16 交通燈的狀态

說到這裡,我們再來看看張逸的陳述。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖17 圖1的部分,加了标注

張逸的陳述④解釋了為什麼他認為“狀态和事件本質相同”,原因之一是“它們都是某個行為産生的結果,并與該行為相關聯”。

我的評論

這中間的邏輯是不成立的。

煉鋼既産出鋼,也産生廢渣。那能不能這樣推論:鋼和廢渣都是某個行為産生的結果,是以這二者的本質相同?如圖18。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖18 鋼錠=廢渣?

更深入地剖析背後的原因,可能是混淆了泛化和關聯的差別。

我以人為例畫出類圖,如圖19。圖中人和大腦、闌尾的關聯可以改為更貼切的組合型關聯。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖19 泛化和關聯的差別

人有男人和女人,說的是集合關系,也就是泛化關系,說男人、女人都是人,本質相同,這個可以。

人有大腦和闌尾,說的是個體關系,也就是關聯關系,說大腦、闌尾都屬于人,本質相同,這個就有問題了。

這可能就是張逸認為“狀态和事件本質相同”,“它們都是某個行為産生的結果,并與該行為相關聯”背後的原因。

我在以前寫的一篇文章中就指出過滕雲 譯、張逸 審的《實作領域驅動設計》中譯本在翻譯時搞混泛化和關聯的問題:猴子掰玉米?比較不同版《領域驅動設計》說“不變式”和“聚合”。

那麼,怎樣的表述是正确的呢?

正确的表述應該是:對象上發生某事件,可能會導緻新增一個對象來記錄此事件的内容,可能會引起狀态變化。

我把Fowler給出的類圖翻成中文,如圖20:

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖20 Fowler在圖8給出的類圖,翻譯成中文版

再畫一張序列圖,如圖21。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖21 事件發生的序列圖

注意我上面陳述中的“可能”和圖21中的opt。

(1)有事件發生,未必需要記錄事件

電梯每天上上下下,不知發生多少次“召喚”事件,但是目前的電梯不會記錄“召喚”事件的細節——誰召喚的、什麼時候召喚的……,當然,也許有一天,電梯有了足夠的計算和存儲資源,就會記錄這一切。

不記錄事件,不代表事件沒發生,更不代表事件沒有産生效果。

(2)有事件發生,未必會引起狀态變化

以圖15的棧為例,假設棧的長度是1000,“空”狀态下發生“壓入”,遷移到“半滿”,再發生“壓入”,遷移到“滿”的警戒條件沒滿足,狀态并沒有變化,依然停留在“半滿”。

可能有人會就說,那是你的狀态不合适,如果把“未健身”、“已健身”、“未壓入”、“已壓入”作為狀态,搞一一對應,不就好了嘛?

哎,有的人就會炮制一些一一對應的“方法學”,然後兜售給需要的人。這些“方法學”的優點是簡單易學,不用思考,産出巨大,是摸魚的上佳選擇。

一一對應的招數可以是:

(1)為每個屬性值配置設定一個狀态

還是以棧為例,如圖22。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖22 一個屬性值一個狀态

如果是圖22這樣,那就确實滿足“有事件發生,就有狀态變化”了。

(2)去往各個狀态的遷移對應各自的單個事件

這應該就是張逸所想象的狀态機,也是許多“事件風暴”得出來的狀态機(雖然他們未必畫圖)。如圖23,去往A-ed的遷移隻能由A事件觸發,去往B-ed的遷移隻能由B事件觸發……

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖23 一個狀态對應一個事件

圖23這樣的狀态機是存在的,例如“報告”的“已受理”、“已初審”、“已複審”、“已終審”。

但如果領域邏輯真的是如此簡單而直接,用不用狀态機來整理領域邏輯都無所謂。

而且,邏輯往往沒有那麼簡單。一個undo事件就可以破壞這個一一對應,它可以讓對象從“已複審”遷移到“已初審”,也可以讓對象從“已初審”遷移到“已受理”。

那廢除undo事件不行嗎?隻保留A、B、C,讓調用者來決定什麼時候A,什麼時候B,什麼時候C。

如果是這樣,不如用下面這個更絕的一一對應:

(3)隻保留“改變狀态”事件

如圖24,調用者通過調用“改變狀态”來讓對象改變狀态,愛怎麼改怎麼改。

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

圖24 隻保留“改變狀态”事件

你看,表面上我有狀态機(高大上!),但又不用做太多思考,受用,爽!

但是,這樣的“狀态機”是沒用的。

為什麼狀态應該是這些而不是那些,事件應該是這些而不是那些?我們就要了解下面的知識:

三、狀态機到底是幹什麼用的

待續……

[2020.01加一套題]UMLChina模組化競賽題大全-題目全文+分卷自測(11套110題)

全程字幕-25套UML+Enterprise Architect/StarUML模組化示範視訊

[新增:鴕鳥]軟體開發團隊的膿包:皇帝的新裝、口号黨、鴕鳥、廢話迷

《軟體方法》書中自測題-題目全文+分卷自測(1-8章)16套111題

怪論:東北公司用用例做需求,反映了東北網際網路落後?

别把洋垃圾當寶貝-評InfoQ中國“靈活……”文章(一)

中文書籍中對《人月神話》的引用(完結,共110本):軟體工程通史1930-2019、實用Common Lisp程式設計……

CTO也糊塗的常用術語:功能子產品、業務架構、使用者需求……[20210217更新]

UMLChina服務介紹

評張逸的“狀态和事件本質相同”(上)-DDD話語批評之一

繼續閱讀