一文教會你領域建模

我們軟件工程師實際上只在做一件事情,即把現實中的問題搬到計算機上,通過信息化提升生產力。

信息化的過程就是從 “問題空間” 到“解決方案空間”的過程。然而,“問題空間”和 “解決方案空間” 之間有一道鴻溝,程序員的任務就是跨過這條鴻溝。說實話,跨過去並不是很難,理論上,再醜陋的代碼也能實現業務功能。

關鍵是跨過去之後,系統的內建質量(built-in quality)會怎樣:一個設計良好的系統,概念清晰,結構合理,即使代碼庫龐大,依然可理解、可維護。而一個糟糕的系統,只能是屎上雕花。 其中,領域概念和領域模型的缺失,是造成這種差異性的罪魁。

1. 爲什麼需要建模

爲什麼說領域概念是否明晰、領域模型是否正確是區分好壞系統的關鍵所在呢?這和我們的大腦結構有關係。

我們人類的大腦天生喜歡 “偷懶”,能動用系統 1 的,我們絕不想啓用系統 2。(系統 1 代表直覺,系統 2 代表理性,出自《快思慢想》)。所以在面對複雜情況的時候,諸如擁有幾十萬行代碼的應用系統,沒有人的大腦能裝下這麼多的信息。唯有將其分而治之,將大問題分解成多個小問題,我們纔有可能理解和實現。

這個分解的過程在我們軟件領域,主要有兩種範式:一個是過程式分解(過程範式),另一個是面向對象分解(對象範式)。

自上而下的過程式分解符合我們的思維習慣,是我們常用的分解形式,非常有用。只是,在問題變得更復雜時,它有一定的侷限性。想象一個應用程序,其代碼有幾十萬行。如果使用 C 語言平均每個子程序編寫 50 到 100 行代碼,那麼子程序就有幾千到幾萬個。如果使用面向對象,平均在每個類中彙總 10 個子程序,那麼類的總數就比子程序的總數減少 1 個數量級,爲幾百到幾千個。當然,即便是 1000 個類,數量依然龐大。爲了可理解性,我們可以按照內聚性,進一步使用 “包”、“組件”、“模塊”、“服務” 做歸類分組,以減少我們大腦的思維負擔。

這就是我們爲什麼需要面向對象的原因,其本質是因爲面向對象技術,相比較過程分解,可以減少構建數量,通過領域封裝(對象封裝、包封裝、組件封裝、服務封裝......)的層級結構,把問題控制在可以理解的範圍,而不至於失控。

可是,我們很多系統還是失控了。因爲他們雖然使用了面嚮對象語言,但乾的還是面向過程的勾當。體現在代碼上,就是缺少對領域概念的清晰表達、缺乏面向對象的領域封裝、領域邊界模糊不清、以及領域模型抽象不正確。

因此,領域建模是應用程序設計中核心的核心。因爲不管是戰略上的基於微服務的領域劃分,戰術上的 OOD,代碼的可讀性,以及關係數據庫的建模等等,都離不開領域概念和模型。

2. 理解業務需求

業務需求是我們工作的起點,只有深入的理解業務需求,理解問題空間。我們纔有可能抽象出相對合理的領域模型,才能構建出相對合理的系統。拋開教科書上那些 “正規” 的需求文檔規格說明書,這裏,我只介紹我認爲有效的需求捕獲方法。

2.1 用戶故事

User story 在維基百科上是這樣定義的。

In software development and product management, a user story is an informal, natural language description of one or more features of a software system

https://en.wikipedia.org/wiki/User_story

跟厚重的需求文檔比起來,User Story 會更敏捷。根據我的經驗,大部分的系統,都可以通過這種方式來表達,雖然會遺漏比較多的細節,但是作爲理解問題的開始,至少不會那麼令人討厭。

User Story一般的寫法是——As a <who>, I want <what> so that <why>。比如我要實現一個博客系統,我就可以這樣寫我的User Story:

  1. 作者<who>登錄博客<what>,so that可以寫文章。<why>

  2. 遊客<who>可以瀏覽文章<what>,so that學習文章的內容。<why>

  3. 遊客<who>可以寫評論<what>,so that發表對文章的看法。<why>

這樣寫的好處是,我們可以用簡短的話語,把一個事件裏面的主要要素<who><what><why>都表述出來,即便如此,我們還可以更簡便一些,有時候<why>如果是顯而易見,或者沒那麼重要的話,也可以不寫。Mike Cohn 也說過類似的話,他說:“‘so that’ clause is optional although still often helpful.” 還是以blog系統爲例,剩下的User story我們可以這樣寫:

  1. 作者<who>打開web編輯器,寫文章,發表博客<what>

  2. 作者<who>可以給自己的文章打上1到多個標籤<what>

除了縮減,我們也可以添加更多的要素,比如根據5W2H,我們可以用As <who> <when> <where>, I want <what> because <why> 。只是<when>和<where>通常是作爲實體的屬性存在的,重要性沒有那麼大,所以常常會被省略,比如同樣的關於上面寫blog的user story,我們也可以這樣表述:

  1. 作者 <who> 晚上7點 <when> 在家用手機 <where> 登錄blog <what>,so that可以寫博客。<why>

這樣寫當然也沒有問題,只不過 “晚上 7 點” 和“手機端”只是作爲 blog 這個實體的屬性而存在,不寫關係也不大。

2.2 業務活動調研

上文中 blog 應用相對比較簡單,我們大部分的業務系統都要比這個複雜。比如供應鏈系統,裏面涉及很多用戶角色和業務流程。此時我們可以通過用戶調研的方式來理解業務。調研的重點一個是用戶角色,也就是 ,另外一個是由這個角色發起的業務活動,也就是 < what>。實際上,跟 User story 關注的重點也是一樣的,只是形式有所不同。

比如,我之前進入到一個零售業務的時候,就會帶上一個小本本,每天請不同角色的人喝咖啡,然後記錄下來他們的業務活動。這對我快速理解這個零售業務起到了很大幫助。

調研回來之後,我將這些活動節點串聯起來,就能得到比較完整的業務流程。我們可以藉助泳道活動圖,理清系統的主要角色,以及他們之間的交互關係。如下圖所示,將行業控商小二和區域控商小二的主要業務活動串聯起來,就能得到日常工作的業務流。

2.3 其它方式

上面介紹的兩種方式都不是需求分析的 “正規軍”,只是我個人實踐中,覺得簡潔又非常實用的方法。當然正規軍的方法也很有用,比如正規的 Use case(有前置條件,異常分支)、或者 UML 的用例圖等手段也可以輔助幫助我們分析業務,理解業務。形式不是重點,能抓到老鼠就是好貓。

有同學可能會有疑問,活動圖、流程圖、時序圖呢?你當然也可以用,只要有助於你理解業務,挖掘重要的領域概念都可以用,只是偏流程和交互相關的,更多的還是在實現階段用,對於領域建模來說,最重要的還是挖掘概念和抽象模型。

3. 開始領域建模

模型是對現實世界的映射。現實世界中的事物在軟件世界中會被映射成實體對象:該事物在現實世界中被賦予什麼職責,在軟件世界中就被賦予什麼職責;在現實世界中擁有什麼特性,在軟件世界中就擁有什麼屬性;在現實世界中擁有什麼行爲,在軟件世界中就擁有什麼函數;在現實世界中與哪些事物存在怎樣的關係,在軟件世界中就應當與它們發生怎樣的關聯。這正是面向對象編程的核心思想,也是領域建模的核心思想

建模的過程就是實現這個映射,跨越鴻溝的過程。其本質是一個整理信息、挖掘概念、發現事物內在聯繫的過程。

3.1 用例分析法

該方法簡單來說就是找名詞(nouns)和找動詞(verbs),複雜點解釋就是通過分析語句,找到裏面的重要領域概念,建立概念之間的聯繫,從而構建我們的領域模型。

使用用例分析法大致需要以下幾個步驟:

  1. 描述問題:
    我們需要用簡短的語句把問題域描述清楚,書寫 User story 或者用例,是建模的關鍵前序動作。

  2. 挖掘概念(Digging out concepts):
    領域概念隱含在語句中,重點關注語句中的名詞(nouns),因爲 nouns 常常意味着重要的領域概念。這一步不容易做到,因爲自然語言有很大的隨意性,很多同義詞、多義詞混淆其中。

  3. 建立關聯:
    尋找關係,需要關注動詞(verbs)。因爲關聯意味着兩個模型之間存在語義聯繫,在用例中的表現通常爲兩個名詞被動詞連接起來。

我們還是以上文的 blog 應用爲例,演示一下這個用例分析方法的工作過程。

一、描述問題

首先我們需要對問題進行描述,整理出如下的 User story:

  1. 作者輸入用戶名、密碼登錄博客,so that 可以寫文章。

  2. 作者打開編輯器,撰寫文章,發表博客

  3. 作者可以給自己的文章打上一到多個標籤

  4. 作者可以給自己的博客設置一到多個分類

  5. 用戶可以發表評論,so that 表達對文章的看法。

  6. 用戶可以回覆他人的評論,so that 發表對評論的迴應。

二、挖掘概念

對於 blog 這個簡單的應用,我們不難從 User story 裏面找到博客領域的相關概念,譬如作者、文章、博客、標籤、分類、評論這些名詞都是這個領域裏重要的概念。

在這個階段,我們可以整理出一個 “核心領域詞彙表”,這個詞彙表將作爲團隊的 “統一語言(ubiquitous language)”。對 blog 這個應用,我們可以得到如下的核心領域詞彙表:

q1uRfN

在覈心領域詞彙表中,至少要包含中文、英文和解釋三個字段,中文術語不必多說,我們日常都在說肯定是需要的;英文術語主要對應的是代碼、設計文檔、數據庫 schema 等但凡需要用到的地方都要以這個詞彙表爲準,並保持一致性;解釋是要說清楚這些概念的含義,可以是文字,如果需要也可以加圖片。

三、建立關聯

實體之間的關聯肯定是有某個動作觸發的,因此,我們要特別關注 User story 中的動詞。在 blog 案例中,像撰寫、打上、發表、設置、回覆這些動詞都有可能建立概念之間的聯繫。比如一個作者可以撰寫多篇文章,所以 Author 和 Article 之間的關係就是 one2many 的關係。

至此,對於 blog 應用,我們的領域建模工作就完成的差不多了。有了這個領域模型,後續的數據模型設計,API 設計,詳細設計都可以圍繞着這個核心領域模型展開了。

3.2 四色建模法

如果我們所有的業務都像 blog 應用這麼清晰明瞭那該多美好。然而更多的業務場景要更復雜,語言表述也要更晦澀。四色建模可以作爲用例分析的補充。因爲,所有建模方法的本質都是一樣的,即從問題域中挖掘領域概念,建立領域模型。四色建模,以及後面介紹的事件風暴也是如此,只是他們提供了更多的一些方法和視角,所以是一種補充。

上圖大家看到的就是四色建模。2、3、4 都不是什麼新鮮玩意,用例分析法的找名詞,基本上就能把它們揪出來。

四色建模的補充作用,關鍵就在於 Moment-interval,也難怪 Peter 會起一個怎麼奇怪的名字,查閱了一些資料才發現,取這個名字也是被迫無奈,正如這篇文章中所說:

Peter Coad called this archetype «transaction»; however, this caused a lot of confusion. He actually meant the purer definition of the word as an exchange or interaction between parties. Because of the confusion, he renamed the archetype from «transaction» to «moment-interval»。

https://curtis.schlak.com/2013/05/09/modeling-in-color-part-iii.html

他本來想取 transaction 這個名字的,但是這個詞容易引起歧義,所以改成了 Moment-interval,想表達的是一種 “交互”(exchange or interaction)。

好吧,既然如此,我們就暫且叫它 “交互對象” 吧。這東西之所以有用,是因爲我們在用語言表述問題的時候,因爲語言的靈活性、歧義性,會經常表達不明確。比如一些重要的領域概念可能會以動詞(verb)的形式進行僞裝。

比如這個用例:People join the club to become members.

join 在這裏雖然是一個動詞,但是它暗含着一個重要的 moment-interval,或者叫 “交互行爲(interaction)”。這個交互行爲實際上是一個非常關鍵的業務信息,即入會申請(application)。只有先入會,才能成爲會員,不是嗎?

這就是四色建模給到我們的啓示,我們的語言會 “騙人”,有時候需要進一步挖掘,從多個角度觀察,多聆聽,多學習,才能把“寶藏” 挖出來。

當然,上面的用例也可以換一種表述方式:people submit an application to the club, so that he can become a member.

如果這樣寫的話,我們就能比較容易的識別出 application 這個重要的領域對象。所以,一切的根源還是在於 “語言”,這也再一次佐證了按照用戶故事(who)——(what)——(why)的方式寫用例會更好,好的需求描述能讓 “其義自見”。

3.3 事件風暴法

“設”和 “計” 都是言字旁,也是在暗示語言和思考在設計中的重要性。事件風暴的好處是,我們可以讓領域專家、產品經理、業務人員等充分討論業務活動、業務流程。聆聽他們的語言,從而有機會在這個過程中發現一些重要的領域知識(領域事件、命令、角色、實體等)。

Eric 在《Domain Driven Design》中也表達了同樣的觀點,他在第九章 “將隱式概念顯性化” 中說道,聆聽語言、閱讀專業書籍是挖掘隱式概念的有效方法。特別要留意領域專家說出來的在我們設計中沒有的 term(術語),這常常意味着一個重要領域概念的缺失。

我覺得時間允許的話,大家可以嘗試玩一下,因爲腦暴的確可以幫助我們思考的更全面,有機會發現一些隱式的關鍵領域概念。即使沒有,這種 “團建活動”,對於增進團隊之間的情感和更深層次的理解業務也是有益的。

4. 總結

語言、語言、語言,重要的事情說三遍。**領域建模就是在分析語言!**從語言中挖掘關鍵的領域概念、建立模型。因此,需求描述很重要,在建模之前,我們要儘量用正確的語言來描述需求(User story 是很不錯的範式)。

之後,我們要深入理解和分析這些語言,雖然 “nouns are objects” 可以解決大部分問題,但還不夠,因此面對更加複雜的場景,爲了挖掘更多的隱式概念,我們可以藉助更多的輔助手段,比如四色建模、事件風暴、專家會談、閱讀領域書籍等等。但歸根結底,都是語言的思維遊戲。

最後,我想說領域建模作爲一種能力,是可以通過《刻意練習》習得和不斷提升的。越多的鍛鍊就能建立越強大的心理表徵能力,屆時,你會發現你已經不再需要這些 “方法” 的指引,更多的時候只是在用“直覺”。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/3x4fK7rtA2US9fxoKpoAiA