運用事件風暴進行領域分析建模


在確定了全景事件流之後,可以在戰略設計層面繼續精進,鑑別出領域與限界上下文的邊界。這裡略過不提,且進入戰術設計階段的領域分析建模。


事件風暴的分析模型要素


通過事件風暴進行領域分析建模,其核心的模型要素就是“事件”。除此之外,參與事件風暴的分析模型要素還包括決策命令、讀模型、策略和聚合。其中,事件和策略已經在探索業務全景的時候進行了初步識別。


決策命令


通觀事件之起因,除了外部系統是直接發佈事件之外,無論是用戶活動,還是滿足某個條件,都需要一個命令(Command)來響應,它才是直接導致事件發生的“因”。在事件風暴中,Alberto Brandolini將命令稱之為“決策命令(Decision Command)”,使用淺藍色即時貼表示。決策命令往往由動賓短語組成,例如Place Order、Send Invitation等。


由於決策命令和事件存在因果關係,因此二者往往是一一對應的。例如,Cancel Order決策命令會觸發OrderCancelled事件,Subscribe Course決策命令會觸發CourseSubscribed事件。正是這種一一對應關係,使得它們存在語義上的重疊,區別僅在於時態。故而有的事件風暴實踐者認為可以在事件風暴中省略決策命令。


我並不敢苟同這一觀點,相反,我反而極為強調決策命令在事件風暴中的重要性,它是領域分析建模的一個重要驅動力,因為通過它連接了用戶、策略、聚合、讀模型和事件,如下圖所示:


運用事件風暴進行領域分析建模


從圖中可以看出,由事件可以驅動出決策命令,在它們之間籍由聚合對象來發布事件。當事件發生後,如果某個策略滿足條件,也會引發決策命令,而用戶在引發決策命令時,需要足夠的讀模型來幫助它做出正確的決策。


那麼,該如何正確地理解決策命令?顯然,Alberto Brandolini使用決策來修飾命令並非空穴來風,因為這一名詞突出了命令往往需要更多的信息來幫助參與者(Actor)做出決策。


參與者是用例圖的設計要素,在事件風暴中,可以認為是對所有事件起因的抽象:用戶、條件滿足(如定時器)與外部系統。其中,外部系統對我們而言是一個黑盒子,不用考慮它是如何觸發了事件,因而可以忽略。因此,參與者在基於業務場景做出決策時,需要如下兩方面數據的支撐:

  • 信息:必須基於足夠充分的信息才能做出正確的決策,提供這些信息的對象被稱之為讀模型(Read Model),在事件風暴中用淺綠色即時貼表示。
  • 策略:根據業務規則,當某個條件滿足時,會觸發一個決策命令,這個業務規則被命名為策略(Policy),在事件風暴中用紫色標籤表示。


讀模型和策略


當決策命令由用戶引發時,可以確認該決策命令的發生是否需要提供足夠的讀模型信息。讀模型是用戶通過查詢(讀)操作獲得的。若不具備這一信息,可能不足以支持用戶執行決策命令。例如買家希望提交訂單,就需要先查看購物車獲得購物車內容,然後才能執行下訂單(Place Order)的決策命令,觸發OrderCreated事件。這時,查看購物車獲得的結果ShoppingCart就是讀模型:


運用事件風暴進行領域分析建模


讀模型是用戶執行決策命令必需的輸入信息,在代碼層面,這些讀模型就是執行決策命令的領域行為所需的輸入參數。用戶發起決策命令的方式是因為執行了某個活動,例如決策命令“提交訂單”實則是因為用戶點擊了“提交訂單”按鈕。用戶活動的執行與用戶體驗(User eXerperience,UX)直接有關。


現實世界的業務場景通過用戶體驗將用戶與讀模型結合起來,把信息傳輸給事件風暴的決策命令。這一過程牽涉到用戶、查詢和命令操作,恰好符合組成用例的要素。若建模人員熟悉用例,也可藉助用例圖來分析。


注意,上圖是將讀模型ShoppingCart提供給Place Order決策命令,而非查詢操作與命令操作之間的交互。有的事件風暴實踐者將查詢操作也納入到事件風暴的模型中,認為是用戶執行查詢操作獲得讀模型後,觸發了決策命令,如下圖所示:


運用事件風暴進行領域分析建模


我認為這樣的模型設計並不恰當,因為它將活動流程圖與事件的因果關係混為一談了。


實際上,活動流程圖反應了現實世界的問題域,事件風暴表現的事件因果關係卻是解決方案域的內容,這是領域建模活動中兩個不同的層次。買家先查詢購物車,然後提交訂單,這是買家的操作流程。但從事件的因果關係看,並非“查詢購物車”觸發了“提交訂單”這個決策命令,而是用戶通過查詢獲得了購物車讀模型之後,由用戶發起“提交訂單”的決策命令,再通過訂單聚合發佈了OrderCreated事件。


“查詢購物車”和“提交訂單”是兩個不同的用戶活動,它們並不具有時序上的連續性,可以認為是兩個獨立的業務場景。由於查詢操作並不會觸發事件的發生,從模型上看,它也不會導致命令的發生,因而在事件風暴中,並沒有查詢操作的位置,而是以讀模型的形式出現。這也變相地促使建模人員在識別用戶活動時,需要分辨該活動究竟是查詢還是命令,有利於CQRS模式的落地。


當決策命令由策略引發時,就表示事件發生後某些數據滿足了某條業務規則。一旦該策略被滿足,就會引起目標對象的狀態變更,然後根據業務規則的規定觸發下一個決策命令。


例如,策略“提交訂單後,一旦超過規定時間未支付,則取消訂單”會觸發Cancel Order命令,從而引起OrderCancelled事件的發生。策略引發的決策可以是自動的,如定時器檢測到支付時間超時;也可以是用戶手動觸發,如用戶登錄時輸入錯誤密碼的次數太多;還可以二者並存,如在取消訂單業務場景中,Cancel Order命令既可以由定時器自動觸發,也可以由用戶手動觸發。


聚合


雖然決策命令和事件之間存在因果關係,但事件並非直接由決策命令發佈,而是藉助一個“媒介”來發布事件。這個媒介就是“聚合(Aggregate)”。聚合在事件風暴中使用黃色大即時貼來表示。聚合劃分了現實世界和模型世界之間的界線。在現實世界,是用戶執行了決策命令觸發了事件;在模型世界,是聚合履行了發佈事件的職責。例如,在電商系統的業務流程中,現實世界的用戶活動是用戶提交了訂單;在模型世界,是Order聚合發佈了OrderCreated事件。


尋找聚合的過程可能是一個艱難的過程。由於聚合是構成領域分析模型的核心要素,識別聚合需要審慎,不要輕易下結論。若未尋找到它,可以先貼上一個空白的黃色大即時貼表示這裡存在一個聚合,但目前還不知道它的名字。


在事件風暴中,我們也可以利用事件來反向尋找聚合。分析事件的特徵,由於它是由決策命令觸發的,意味著事件的產生會帶來目標對象狀態的變化。狀態的變化分為三種形式:

  • 從無到有:意味著創建,例如“訂單已創建”事件標誌著新訂單的產生;
  • 修改屬性值:意味著值的更新,例如“訂單已取消”事件使得訂單從之前的狀態變更為“已取消”狀態;也可能意味著內容的變化,例如“商品被加入到購物車”事件,說明購物車增加了一個新的條目;
  • 從有到無:意味著刪除,不過在多數項目中並不存在這種狀態變化;表面是刪除,實際是修改屬性值。例如“會員已註銷”事件和“商品已下架”事件,實則都不是直接刪除會員和商品記錄,而是將該記錄的狀態置為“已註銷/已下架”狀態。


顯然,發生狀態變更的對象有很大幾率就是我們要尋找的聚合對象。畢竟聚合對象承擔了發佈事件的職責,而事件又是由於狀態變更而產生。誰能準確地偵知狀態是否變更以及何時發生變更?我想,只有擁有狀態的聚合對象自身才具備這一能力。

事件風暴的建模過程


顯然,圍繞著“事件”為中心,事件風暴給出了一條有章可循的領域分析建模路徑。領域分析建模的基礎是探索業務全景的產出物,即業已識別出來的事件流,以及參與事件流的用戶、策略與外部系統。整個領域分析建模的過程如下:


第一步:挑選任意一個與用戶有關的事件,反向驅動出決策命令,該用戶就是發出決策命令的人(角色)。從事件驅動出決策命令非常容易,就是將事件的過去時態轉換為動賓形式的決策命令即可。


第二步:根據決策命令與事件之間的因果關係,推導出要發佈該事件必須的前置信息,即決策所需的讀模型。讀模型通常由用戶通過查詢操作獲得,可以理解為是決策命令行為的輸入參數。


第三步:根據事件狀態變更的目標,決定決策命令與事件之間的聚合對象。若無法確定,則保留一個空的黃色即時貼,待以後確定。


第四步:選擇當前事件的後置事件。若後置事件仍然與用戶有關,則重複第一步;若後置事件與外部系統有關,可以跳過該事件的建模,繼續選擇下一個後置事件。若事件與策略有關,在進一步細化策略對象之後,驅動出決策命令,重複第三步。


以前面所示的信用卡開卡事件流為例,我們依次選擇以下三個事件:


運用事件風暴進行領域分析建模


首先是審批人參與的“開卡申請已審批”事件,執行第一步,由該事件可以反向驅動出決策命令“審批開卡申請”。


第二步是根據決策命令推導出觸發事件需要的讀模型。審批開卡申請的前置信息是“申請”和“用戶徵信”,若缺乏這兩個信息,審批人無法做出“審批開卡申請”的決策。


第三步是確定決策命令與事件之間的聚合對象。顯然,“開卡申請已審批”事件影響到的就是申請的狀態,它就是我們要尋找的聚合對象:


運用事件風暴進行領域分析建模


接著進入第四步,選擇下一個後置事件“卡號已生成”。該事件與策略有關,細化策略為“卡號規則”。由事件驅動出決策命令為“生成卡號”,進入第三步,識別兩者之間的聚合對象。卡號的生成影響了信用卡的屬性,可以認為該事件影響狀態的目標對象為“信用卡”:


運用事件風暴進行領域分析建模


繼續第四步,選擇下一個後置事件“信用卡製作完畢”。由於該事件由外部系統發佈,可以忽略該建模過程,僅僅標記外部系統即可:


運用事件風暴進行領域分析建模


通過這個簡單案例,可以清晰地看到我總結的領域分析建模過程具有一定的可操作性。事件風暴工作坊的參與人員可以按照建模步驟一步一步執行。執行每一步都需要團隊與領域專家進一步討論和確認,保證識別出來的模型對象遵循該領域的統一語言。


在這個分析建模過程中,每個模型對象都有著建模的參考依據,包括模型對象的身份特徵、彼此之間的關係、承擔的職責,這就在一定程度上減輕了對建模人員經驗的依賴。


分享到:


相關文章: