領域建模的體系化思維與6種方法論

背景

軟件工程師做的核心事情就是對現實世界的問題進行抽象然後用計算機的語言對其進行重新刻畫,在通過信息化來提高生產力。而這其中一個關鍵環節就是如何對問題域進行建模,在過去的工作中經常遇到一個問題是前期因爲業務比較簡單所以設計的模型在支撐時沒有發現什麼問題而隨着業務複雜度的增加就會發現需要對模型進行升級,如果是對模型關係維度的新增那還好,而如果是對原有模型關係的重構,那將會變的非常困難。本文希望能夠通過總結過去自己對領域建模的一點粗淺經驗給需要的同學能有些許啓發,少走彎路。

總體思想

關於如何建模並不是一個單一維度的問題而是一個體系化的工程,我們需要對其進行拆解然後逐個攻破,如何建好模並能順利落地可以拆分爲四個子問題,1)對需求進行功能建模 2)對業務進行領域建模 3)將領域模型映射到代碼模型 4)根據代碼模型落地數據模型。

  • 需求模型:通過和產品及業務同學的溝通,結合行業經驗和知識,明確用戶的真實需求;
  • 領域模型:基於需求模型,提煉出領域相關的概念,爲後面的面向對象設計打下基礎;
  • 代碼模型:以領域模型爲基礎,綜合面向對象的各種設計技巧,完成類的設計;
  • 數據模型:以代碼模型類及類之間的關係,用ER圖刻畫數據的底層存儲關係;

還有一個重要的認知,領域建模並不是萬能的,比如你做的系統很簡單,使用數據庫的CRUD可能一個更好的方式(如上圖中的虛線箭頭),如果做的是基礎架構,比如開發一個RPC框架,也不需要用領域驅動。運用領域驅動的前提一定是業務足夠複雜且多變,需要系統靈活支持。下面這個圖供參考:

需求建模

萬事開頭難,需求是項目最開始的輸入,肯定是非常重要的,但現實情況往往是當我們接到需求後第一個想到的問題確是我該如何實現,而沒有在業務思考這一塊過多的停留。如果最開始理解的輸入是錯誤的,後面的過程再優秀可想而知也只能是垃圾。爲了解決這一問題,很多優秀的大佬發明了很多對需求進行功能建模的工具,主要目的就是讓我們更好的理解需求並認識其背後的本質。如常用的工具有5w2h、業務活動、用戶旅程、商業畫布等。這些在網上都有很多的資料,只舉幾個例子,不做重點介紹。

  • 業務活動

  • 用戶旅程

  • 商業畫布

領域建模

下面的每一種方法都是從某一個角度幫助我們更好理解客觀事物,所以沒有哪一種方法是萬能的,在實際項目中常常要多種方法共用,纔可能認清事情的全貌。

四步建模

在我們日常工作的項目中,大多數場景都可以通過在需求用例中通過找名稱的方式來最終刻畫出領域模型,當然找到的名詞後並不是所有的都符合要求,這時可以通過一條原則"一個名詞或實體必有其屬性和行爲,一屬性或行爲也必歸屬於一個實體"來進行提煉,不符合這條原則的名詞就是需求剔除的。總體來說建模一共分爲四步

  • 選名詞:從用例中選出所有名詞,在去僞存真選出符合要求的;
  • 找動詞:找出所有動詞,在判斷這些動詞屬不屬於上一步選出的名詞所具有的行爲;
  • 加屬性:找出所有屬性,在判斷這些屬性屬不屬於上一步選出的名詞所具有的特徵;
  • 連關係:確定實體和實體之間的協作關係;

案例:用戶購買商品

需求用例

用例名稱 用戶購買商品
用例描述 1、用戶在購物app上選取多款商品進行下單2、通過用戶檔案獲取用戶名稱、地址等信息3、從不同商家購買的商品生成不同的子訂單;記錄商品及其數量,並彙總付款金額4、保存訂單5、選擇一種支付方式(包括銀行卡、支付寶、微信)並進行支付;超過30分鐘未支付自動取消訂單
約束和限制 1、一個商品單次購買不能超過30單2、單次支付金額最大不能超過99999RMB

建模步驟

第一步:選名詞。從用例上選的名詞如下:用戶、購物app、商品、用戶檔案、用戶名稱、地址、商家、訂單、子訂單、支付方式、銀行卡、支付寶、微信。通過這種方式可以很輕鬆的識別領域中的相關概念,但選取的名詞並不一定都是領域相關的,所以接下來還需要進一步的提煉。提煉過程

  • 刪除"購物app":購物app只是一個功能的載體,並不屬於購買商品流量裏的一個領域概念,所以刪掉
  • 刪除"用戶名稱" :用戶名稱只是用戶的一個屬性,並不是領域概念
  • 刪除"地址" :地址只是用戶檔案的一個屬性,並不是領域概念
  • 刪除"銀行卡、支付寶、微信、支付方式":銀行卡、支付寶、微信屬於支付方式的一種具體形式,而支付方式可以歸屬爲訂單的一個屬性,並不是獨立的領域概念

所以最終提取的領域實體是:用戶、商品、用戶檔案、子訂單、訂單、商家

第二步:找動詞。從用例上選的動詞如下:選取、彙總、下單、保存、支付、取消

找動詞的目的是反向檢查是否有遺漏的實體沒有提煉出來,因爲有些隱含的概念並不一定能在用例裏找到,且一個動作必歸屬於一個實體。如果有發現動作沒有歸屬實體只有2種情況,一是這個動作不屬於這個領域,二是有遺漏的實體沒有提取出來。經過分析 "選取" 是用戶主觀的一種行爲,並不屬於這個領域所以刪掉,"下單、彙總、保存、支付、取消" 都屬於訂單的動作。

第三步:加屬性。理論上產品同學要在用例上把模型的所有屬性全部列出來,但現實情況不一定能做到,這時除了用例還需要當面和產品對焦清楚各個模型的屬性。

名詞 屬性
用戶 用戶名稱、性別、證據類型、手機號
商品 商品名稱、單價、商家、商品類別、商品編碼
用戶檔案 國家、省份、地市、區縣、地址詳情、聯繫電話
子訂單 商品、數量、單價、金額
訂單 用戶、地址、金額、下單時間
商家 商家名稱、經營地址、經營範圍、聯繫人、聯繫電話

第四步:連關係。關係主要表達模型和模型之間怎樣協作

補充場景:比如 "一個客戶想入駐平臺成爲一個服務商",按照上述方法提煉出動詞"入駐",但會發現其並不適合成爲"客戶"、"平臺"、"服務商"實體的行爲,因此可以反向推出還應存在另外一個實體"入駐申請單"的概念,"入駐" 這個動詞更準確的叫法是"申請入駐"。

歸類分組

歸類分組,就是把具有相似性或相關聯的信息,按照一定的標準進行分類,歸爲同一個邏輯範疇。“類”是一個極其重要的概念,可以看作本質相同或相似的事物的集合。分類就是按照一定的標準,根據對象屬性、特徵的共同點和不同點,將對象劃分爲不同的種類。分類時,需要對這些類別進行鑑定、描述和命名。

分類在生活中無處不在。通過分類,一方面可以把雜亂無序的事物區分開來;另一方面還要賦予不同類別以穩定的、概念化的名稱。一個好的命名,能夠簡潔有力地表述事物的本質。分類的過程,就是探尋事物和問題本質的過程。

如何用歸類分組進行領域建模可以分爲3個步驟

第一步:定義要建模的領域問題:也就是要清楚我們要解決的問題是什麼?

第二步:對領域問題進行拆解:對問題進行分析拆解,形成平鋪的多個子問題,此步驟可以儘量發散

第三步:歸類分組:對子問題進行歸類,剪枝,將趨同的子問題,合併成一類(可以理解問產出實體)

案例:生活服務類商品

生活服務類商品場景

  • 預定類商品(KTV預定、休娛預定、麗人預定等):
  • 可以按照週期設置每週的價格;支持設定節假日的價格;
  • 業務特點:商品價格同樣呈週期性變化,有特殊日期以及不可用日期;計價方式包括按人、按間

  • 場館預定類

歸類分組建模過程:

  • 第一步:定義要建模的領域問題:從上面的售賣商品場景可以看出一個商品有多個價格;而決定這個價格的是由一系列的因子決定的,有可能是0個也可能是多個;如何用一套完備的價格模型來支持這些場景是需要解決的問題;
  • 第二步:對領域問題進行拆解:羅列出所有商品價格的場景;

  • 第三步:歸類分組:
    • 按人羣、日期、階梯、座位等類型,可以統一抽象爲價格的不同"維度"
    • 原價、起拍價、保證金、評估價等可以看做室價格的不同類型

所以一個價格由什麼決定:sku的價格 = 價格類型 + 價格維度 + 具體價格,具體抽象過程如下:

  • 第四步;產出領域模型

案例:本地通商家成長

商家成長:爲不同層級的商家門店下發不同類型的任務,完成對應的任務,商家會獲取到對應的權益,進而幫助商家在平臺的不同階段能夠更好的成長。

  • 第一步:定義要建模的領域問題,針對門店維度爲商家建立一套任務體系,不同的門店可以下發不同的系列任務
  • 第二步:略
  • 第三步:
    • 基礎裝修、商品優化、服務評價等屬於一個維度,可以抽象爲"任務流"
    • 門店圖、手藝人、是否有品牌故事等屬於一個維度,可以抽象爲"任務"
    • 而由"門店圖、手藝人"等任務組成的基礎裝修,可以抽象爲"任務組"
    • 門店圖片裏又包含指標和完成的跳轉動作,可以分別抽象爲"任務指標","任務動作"
  • 第四步;產出領域模型

事件風暴

爲什麼引入事件風暴。事件風暴之一的作用就是拉通業務方、產品、研發、測試對業務知識的統一理解,避免各方理解誤差。但在實際操作中受限於各方時間協調的難度及領域專家的角色的缺失,事件風暴往往作爲理解業務,領域建模及領域劃分的利器去使用。

什麼是事件風暴。事件風暴是一種以工作坊的方式對複雜業務領域進行探索的高效協作方法,事件風暴強調以事件驅動團隊探索分析業務領域。

  • 一種捕獲行爲需求的方法
  • 強調以先發散後收斂的方式開展
  • 相關干係人協作的方式進行
  • 注重對領域事件的識別

事件風暴相關元素

  • 事件:重要且已發生的事情。命名方式:完成時+被動語態=賓語+動詞過去式
  • 命令:產生事件觸發的動作
  • 角色/執行者:觸發命令的主體,包括:人員、系統、定時任務
  • 讀模型:執行者決策執行命令時參考的視圖元素

事件風暴操作流程

  • 識別重要事件
  • 識別命令
  • 識別執行者和讀模型
  • 識別領域對象
  • 產出領域模型

案例:銷售基礎

產品截圖

  • 識別重要事件

  • 識別命令

  • 識別執行者和讀模型

  • 識別領域對象

這裏說的領域對象,是從命令、領域事件、執行者、查詢數據裏找到的名詞性概念。例如,對於簽訂合同這個命令而言,受到影響的名詞性概念是“合同”;類似地,對於合同已簽訂這個領域事件,是由於“合同”這個名詞性概念的狀態變化所導致的。

  • 產出領域模型

四色建模

關於四色建模的概念我們可與追溯到90年代,是被廣泛使用的一種系統分析方法。四色建模的目的是要對目標業務系統進行分析並通過不同的顏色標示出人,事,物,角色,通過四色建模得到四色原型圖,每個原型圖有屬性和連接(關聯與依賴等關係)兩個部分組成。

那四色原型具體是哪四色呢?一起來看看

  • 時標原型(Moment-Interval Archetype,簡稱MI)
    • 表示事物在某個時刻或某一段時間內發生的,如銷售訂單、收款記錄等,使用淺紅色表示。
  • PPT原型(Part-Place-Thing Archetype,人/事/物原型,簡稱PPT)
    • 表示參與扮演不同角色的人或事物,如商品、賬戶、店鋪等,使用淺綠色表示。
  • 角色原型(Role Archetype,簡稱ROLE)
    • 抽象了一種參與方式,由人或組織機構、地點或物品來承擔,如客戶、商家、財務組織等,使用淺黃色表示。
  • 描述原型(Description Archetype,簡稱DESC)
    • 對上述顏色表示的內容進行解釋,用於分類或者描述建模過程中產生的數據,事件,或者活動,使用淺藍色表示。

用一句話來概括四色原型就是:一個什麼樣的人或物品以某種角色在某個時刻或某段時間內參與某個活動。其中“什麼樣的”就是DESC,“人或物品”就是PPT,”角色”就是ROLE,而“某個時刻或某個時間段內的某個活動”就是MI。

接下來使用四色建模法來分析領域模型,總共分爲四大步:

  • 建立時標原型:尋找需要追溯的事件,根據追溯事件尋找足跡
  • 建立PPT原型:豐富模型,尋找時標原型周圍的人/事/物,使它可以更好地描述業務概念
  • 建立角色原型:進一步從中抽象出可以參與到不同流程中去的角色
  • 建立描述原型:把一些信息用描述對象補足

案例:商家諮詢

產品截圖

梳理諮詢業務時序,任何的業務事件都會以某種數據的形式留下足跡。我們對於事件的追溯可以通過對數據的追溯來完成,當我們把這些數據的足跡按照時間順序排列起來,就可以清晰的推測出過往的一段時間內發生的事情。

通過對業務的梳理可以產出了四色原型圖的關鍵業務時刻,如下面表格

關鍵業務活動 關鍵業務時刻對象 備註
註冊到店app賬號 用戶賬號信息  
建羣 羣關係記錄 用戶點擊諮詢會自動創建羣
聊天 羣消息 通過羣聊實現不同客服的接待
打電話 電話記錄 用戶也可以通過打電話直接諮詢商家
留資 客資信息  
視頻面診 面診記錄  

通過上面的表格大概知道用戶在IM應用中的關鍵業務時刻和對象

 

尋找關鍵業務時刻對象周圍的人、事、物對象

 

從人,事,物中抽象出角色

 

把一些描述信息用對象補足

 

產出領域模型

 

限界筆紙法

限界筆紙法起源於thoughtworks,由thoughtworks大佬提出的基於四色建模的改進方法。在“四色建模法”的“時標對象”的基礎上確定"限界上下文”與“聚集”的概念,再使用“紙和筆來管理”的方法,力圖在建模過程中實現“分而治之”,增強數據的完整性,並避免過度設計。

建模步驟

  • 根據“業務發生時刻”的價值識別核心領域(core domain)
  • 確定核心領域之間的依賴關係
  • 用紙和筆畫表格並寫實例(這裏的實例可以是業務用例,用戶故事,或者業務發生時刻)
  • 確定“聚合根 (AGGREGATE ROOT)”
  • 以“人以羣分”的原則抽取新的“聚合”

分析模式

學會了上面的建模方法就有能力應對開發中等難度的系統了。不過,如果遇到更復雜的業務領域,還需要更深入的建模技能。《分析模式》是 Martin Fowler 寫的一本領域建模的專著。講述各種分析模式和輔助模式,專注於面向對象分析與設計的結果——模型本身,給出了來自金融貿易、測量、財務以及組織關係等多個領域內的一系列模式。書中每個模式都包含了設計背後的原理、使用的規則以及實現的技巧,給出的例子包含了有用模型的細節,並介紹了用於提高分析、建模和實現的重用技巧。研究不深感興趣的可以看下。

代碼建模

經過領域模型的分析後,面向對象已經初具雛形,但領域類並不能指導我們進行編碼工作,因爲領域類只是從用例模型中提煉出來的反映業務領域的概念,並不是真正意義上的軟件類,我們要需要在進一步,完成領域類到軟件類的轉換,這就是代碼建模階段的主要任務。那麼具體如何做的

  • 第一步:完成領域類到代碼類的映射
    • 類:領域模型中的類直接映射成代碼裏的類
    • 屬性:領域模型類的屬性直接映射成代碼類裏的屬性
    • 動作:領域模型類的動作直接映射成代碼類裏的方法
    • 狀態機:領域模型裏的狀態映射爲類的狀態機
    • 協作關係:用UML的類關係圖重新刻畫協作關係

以上面的一個領域模型爲例

  • 第二步:應用設計原則和設計模式。完成了第一步,類就出來了,屬性和方法也有了,是不是就結束了呢,如果按照設計原則的衡量標準,會發現第一步只是最基本的要求,而不是一個好的設計。還以"用戶購買商品"案例爲例,支付的方式分爲支付寶、微信、銀行卡,運用設計模式後,代碼模型變成如下:

數據建模

數據模型推導的來源是領域模型。一般原則,領域模型中的實體映射爲數據庫中的表;領域模型中的屬性,映射成表中的字段,同時還要根據需求補充更多的字段。模型中的一個一對多關聯,可以映射成一個外鍵字段,以及一個外鍵約束。但一般不會真的建立外鍵約束,而外鍵的邏輯關係還是存在的。可以用虛線箭頭表示這種邏輯上的外鍵關係,稱爲虛擬外鍵。對於多對多關聯,必須增加一個關聯表,其中包括了兩個實體表各自的主鍵。另外,關聯上的多重性決定了外鍵字段的非空約束。

怎麼判斷模型好不好

  • 領域模型是生活中模型的映射。在幾十年前,我們沒有計算機,但是我們仍然可以通過白紙黑字的方式完成我們的業務。換句話說,我們的業務系統只是把一個紙質的單據變成了電腦裏存在數據庫裏的東西。也就是說,我們的模型,一定是在生活中有實際的對應的單據,而且名字也應該是一致的。千萬不要臆造概念。
  • 領域模型不是DB模型。領域模型不是DB模型,一個領域對象可以有多種方式映射到數據庫,但是我們聊領域模型的時候,一定不是聊DB模型,重要的一定是描述清楚業務概念。在做ORM的時候,如果有繼承關係需要的實現的時候,我們再考慮是單表繼承(Single Table Inheritance)還是實體表(Concrete Table Inheritance)等模式。
  • 領域模型的關係一定是穩定的。當接到一個新的需求時,如果發現模型的關係不匹配了,那說明之前的模型設計的肯定存在問題,好的模型實體和實體之間的關係一定是穩定的,只會新增不會對原有關係進行改變。

領域模型爲什麼要一直持續迭代

要在"變化的現實世界中尋找不變性",希望尋找到一個穩定的領域模型,讓系統流程可以靈活改變,模型不怎麼變,但在實際中卻很難完美的做到,這是爲什麼呢?(備註:這裏的迭代不是指對原有模型關係的重構,而是對模型新關係的升級)

  • 意識問題。在用戶、產品人員、運營人員眼中,溝通的語音是"流程"而不是"模型"。開發人員在與他們的溝通過程中,慢慢就形成了以"流程"爲主導,而不是以"模型"爲主導的思維方式。這使得整個開發過程是"流程驅動",而不是"領域驅動"。大家在討論業務與系統解決方案的時候,大部分時間都花在了業務流程、業務規則上,而不是深刻挖掘流程背後的不變因素。
  • 現實世界的複雜性。業務也就是我們的現實世界,灰度的、模棱兩可的東西,比計算機的世界多得多,變化也多得多。很難確定有哪些東西是不怎麼變的,什麼東西是容易變的,而這恰恰是做建模的前提條件。
  • 迭代速度。在穩固的模型,也不可能一成不變,畢竟現實世界一直在變。當現實世界變化到模型不能支撐的時候,要能馬上修改模型纔行。但實際情況是,因爲開發效率的原因,工期趕不上,然後就會在舊的模型上進行打補丁,補丁一個接着一個打,最後整個系統臃腫不堪,開發效率進一步降低,如此惡性循環。
  • 火候的掌握。領域模型是要對現實世界建模,既要去尋找不變性,又要爲可能變化的地方留出擴展性。什麼地方是不變的,要作爲基礎;什麼地方是易變的,要留出擴展性,這其中並沒有一個標準原則。另外,各家公司的業務規模、速度不一樣,團隊實施能力也不一樣。所以在實踐中,要麼會"缺乏設計",要麼會"過度設計"。對火候的掌握,需要有悟性。只有反覆思考,反覆推翻自己之前的想法,再重建新的想法,才能在實踐中不斷找到領域模型、業務發展速度、技術團隊能力之間的"最佳平衡點"。

背後需要修煉的思維

上面講的這些都是術的表現,是借假修真,只有"真"纔是我們修煉的道,也就是做事的思維,下圖列舉一些,歡迎感興趣的多交流。

作者 | 修冶

點擊立即免費試用雲產品 開啓雲上實踐之旅!

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章