域模型之二,主要概念

郭東白發表於2016-02-22

這篇文章是一系列文章的第二篇。 如果還沒有閱讀過第一篇請從這裡開始。 另在一月八日做了小改動, 增加了域語言(Ubiqitous Language)的定義。 

這篇描述可能比較枯燥的一篇。 不過這篇可能是大家最需要花時間理解的。 這些定義不是從某個文獻上的摘抄,而是我根據自己的理解和我在阿里看到的一些建模中經常出現的問題, 而特別強調每個概念中經常被同學們忽略或者是誤解的地方。   

這裡先重複一下域模型的定義:域模型是為了準確定義需要解決問題而構造的抽象模型。 這個抽象模型中最核心的概念就是實體(Entity)。 

域(domain:前面提到了, 我們討論的域是問題域。就是我們要解決問題的邊界。 比如說電子商務領域, 就是任何通過某種電子媒介而使得買賣雙方完成遠端交易的過程中需要解決所有問題的彙總。

子域(subdomain:一個大的問題域又會被遞迴的分割為多個小的問題域。 比如說電子商務領域又會有和任何商品相關問題的商品域, 在這之下有商品釋出的問題域, 也就是包涵任何與釋出商品有關的速度,效率,準確性,流程,規則等等相關問題的總和。在商品釋出的這一個子域之外,又會有一個相關的商品稽核域, 用來解決任何和商品釋出過程中和商品的智慧財產權,禁限售, 賣家的區域性授權等等相關的稽核問題。  

語境(context:是一個特定人群在討論的問題域是所形成的上下文。 這裡要強調一個概念, 特定人群不是以團隊或者是專案為邊界劃分的人群, 而是以知識為邊界來劃分的人群。 也就是說上下文不是普遍存在的, 而是存在於一個人群內部的,並且這些上下文大多是以隱形知識(Tacit Knowledge)的方式而存在的。  什麼是隱形知識呢, 就是還沒有被總結整理歸納沉澱的知識。 舉例來說說AliExpress商品團隊會有一個獨立詳情頁和商鋪詳情頁的概念, 這兩個概念的準確定義和他們的商業目標的定義目前還沒有顯現化。 但是幾乎團隊內的每個人都能分別出這兩個概念和他們所關聯的問題。 這就是一個語境(Context)。

特定語境(Bounded context:是把上下文限定到某個特定的邊界之內。 這個邊界是由某個特定人群和他們所討論的問題子域來決定的。 舉例來從電子商務,到商品, 到商品釋出, 到規則,到商品稽核規則是從大到小的問題域,他們對應的特定語境就是越來越細分並且越來越準確的上下文, 而對應的人群也是越來越小並且越來越專業。  最常見的bounded Context就是某個國際標準組織, 他們會為自己的標準定義專業的名詞, 語法, 和表達方式。 而一個大的標準,比如說Health Level 7 (HL7), 總共有數千的會員。 這些人又組成了更小的標準委員會。標準委員會內又有更小的討論組。 這就形成的從大到小的分級。而另外一個醫療標準, DICOM,又形成了另外一個bounded context。

語境對映(Context Mapping:不同的語境之間會有互動, 那麼從一個語境到另一個語境的翻譯過程就是語境對映。 比如說剛才提到的HL7是一個醫療標準。 而DICOM是一個醫療影像標準。 每個語境有一套完整準確的定義。 那麼當某個場景需要我們在兩個語境之間做互動的時候, 我們就需要Context Mapping 了。 舉個例子來說,一個普通咳嗽的病人可能會被醫生推薦去做胸透, 那麼資訊就需要從一個只能理解HL7(醫療語境)的系統交換到DICOM(醫療影像語境)的系統。 那麼在HL7系統中的模型就需要無損的對映到DICOM系統中去。 這就是個對映的例子。     

域語言(Ubiquitous  Language)是一個團隊在某個特定語境之下建立的交流語言。 這個語言有兩個要求:第一:特定語境。 這個語言不是自然語言, 而是在某個語境之內的一個特定語言。 任何一個團隊或任何一名同學都可能橫跨多個域,這些域內的各種概念可能不是顯性化的,並且某些名詞有可能會是重疊的。 比如說兩位同學交流前端問題時在使用的域語言和這兩位同學交流商家准入門檻時使用的域語言其實是兩個域語言。 第二:準確無歧義。也就是在這個語境之下, 交流雙方任何一個成員對某個名詞,描述,上下文的理解和另一個成員的理解是一樣的。 比如說“優質商品”這個詞在我沒有給出定義之前, 每個同學的理解都不一樣。 但是在一個AliExpress燈具行業,這個定義就不存在歧義, 同學們甚至可以立即拉一個列表出來。 域語言這個概念是Eric Evans發明的。 可惜有點用詞不當。 他給這個概念起名為 Ubiquitous Language。 而Ubiquitous 是無時無處不在的意思,也就是Omnipresent。 這與域語言的概念恰恰相反。 造成了大家理解上的困難。  個人認為應該叫“Domain Language”更合適。  

 

實體(Entity):是一個可以唯一標識的個體。實體到底是什麼是一個哲學家爭論了兩千年的概念。我們不在此做更多的討論。 但是在我們說討論的計算領域裡, 我個人認為有幾個特性應該是普遍存在的。 第一, 實體可以是抽象也可以是客觀存在的。舉例子來說:一個人, 一個虛擬的角色,一個人群的畫像, 一個人群的所認可的某個理論,都可能成為域模型中要討論的實體。(說到這裡, 我們傳統的計算機領域對entity一詞的翻譯“實體”實質上不準確, 我覺得“個體”是更準確的用詞)。 第二,我們討論的任何個體是有其生命週期的。 它有個從不存在到存在,然後最終到不存在的過程。 首先說實體是有狀態的。 並且往往實體在這些狀態自己的轉化是有實體之外的事件(Event)所觸發的。 瞭解實體狀態和觸發事件其實是個認知一個實體非常關鍵的一環。這裡的另一層含義是實體不是脫離個體而存在的執行規律。 用程式來打比方, 我們的實體不是計算邏輯, 而是被這些邏輯所操縱的物件。 從現實世界的角度來說, 實體可以是萬物。 而主宰萬物執行的自然規律“道”卻不是實體。    第三,我們討論的任何個體在我們的問題域之內是可以被單獨的分辨出來的。這裡我想強調一點,就是個體在一個特定的問題(子)域中可以被分辨, 而不是沒有任何限定範圍內的可以被分辨出來(Entity is Individually identifiable within the problem (sub)domain)。  舉個例子, 在商品釋出這個子域內, 我們可能有釋出失敗這個Event。 這個Event可能會有一個失敗原因來描述。 失敗原因的一種可能是”稽核未通過”。 從釋出這個角度來說,也許這就是我們對商品稽核需要了解的最細粒度, 這裡沒有提到任何具體的實體。  然而在另外一個相關的商品稽核域中, 我們需要描述具體是什麼稽核的動作導致了釋出的失敗。 稽核域需要區分每個稽核動作發生的時間, 內容,詳細描述 等等, 也就是說每個稽核動作在商品稽核域中是一個獨立存在的實體。        

事實上我看到最頻繁的域模型設計糾結就發生在對實體的認知上。在解決一個問題的時候到底我們需要考慮什麼實體經常是域模型設計最關鍵的一環。 後面我會介紹一下具體的設計思路。

觸發事件(Domain Event是引起實體狀態變化的事件。 觸發事件客觀存在, 有生命週期, 可以單獨分辨,所以它本身也是一個實體。 但是觸發事件並不一定需要被顯性建模。 往往某些觸發事件比如說使用者註冊,被當作實體外的執行規律的一部分。 但是我個人建議大家還是要描述觸發事件,雖然不一定要在最終的Model Diagram裡畫出來。 

數值物件(Value Object簡單來講就是常數物件, 比如說一個數字, 一段文字, 某一組引數。這些常數可能遞迴的組成一個複雜的object。 但是從問題域的角度上來說, 這些常數不存在一個生命週期。  舉個賣家系統的例子, 比如說我們需要解決商戶註冊的問題。 這個過程中我們可能發現如果我們為每個類目的商戶設計一個模板, 就可以幫助使用者提升註冊速度了。 這個模板, 雖然很複雜, 但是從商戶註冊這個問題域來說, 它就是一個數值物件。 

資料物件可以是一個相對的概念。  在一個問題領域的數值物件可能是另一個問題領域的一個實體。 繼續上面的例子,  隨著我們商業擴充, 我們可能在很多國家建立很多市場, 每個市場可能很多類目, 甚至有些為這個市場定製的特定的類目, 而每個類目下我們會吸收很多的賣家。   在這種複雜商業環境下, 我們可能發現模板管理成了一個新的問題域。 在這個問題域下, 每個模板都能夠被客觀存在, 獨立識別, 並且有生命週期, 所以模板成了問題域內的一個實體。 這其實就是大家最常見的領域建模過程中的糾結。   

實體型別(Entity Type:實體型別是某一類實體的聚類。 事實上我們很少把建模精力花在研究某個實體上,我們大多數時間在研究實體型別。 簡單來說我們是在研究Class的定義,而不是研究某個object的定義。  這又是領域建模過程中又一個糾結點。 因為我們在談論一個實體的時候, 我們是沒有歧義的。 但是一旦抽象到型別,我們抽象的邊界就是一個很大的問題。 舉個例子,我們做電商都會有買家這個實體型別。 但是真正買家型別的定義我們很少深究過。但是仔細想一下, 1688, B2B, 天貓,淘寶,淘海外, 鹹魚, 貓超, 聚划算, AliExpress,每個阿里的電商子域的買家實質上大有不同。 這些買家類不是一個覆蓋全電商領域的買家型別, 而是不同語境下不同的買家子類。 所以我們必須準確定義我們的問題邊界, 在這種情形下是商業邊界,才能準確的定義我們的實體型別。

關係(Relationship是實體型別(相當於Class), 而不是實體本身(相當於Object),之間的關係。 所以關係也會有屬性, 也有例項。關係例項的特例有時候不能被關係型別所不能包涵的的情形。後面我們會提到具體的例子和應對方法。

場景(Scenario場景是問題域中的一個問題例項。 也就是說我們要解決的具體問題。 對Scenario的深度理解建議大家參考一下Scenario-Based Engineering的書籍。  Scenario生成是把一個抽象的問題轉化成生動的例項。 舉個例子,一鍵支付這個問題我們可以有兩種方式表達, 一種表達是把一鍵支付這個事情抽象的描述成一個提升買家便捷性的流程。而另一種表達是把這個問題描述成發生在某個典型使用者(Persona)的一個故事。 這個故事是這個典型使用者在沒有便捷支付的舊社會和有了便捷支付的新社會的兩次不同經歷。 這個故事有幾個作用, 一是簡化交流;二是鎖定問題; 三是鎖定目標使用者,四是鎖定核心價值。 這個故事不應該討論競爭對手, 實現方式, 以及是否團隊能力匹配。  事實上, 這是我們做領域建模需要首先花精力的地方。 一個建模的技術或者是產品的同學, 和一個做業務的同學, 或者是法務,服務,合作方, 賣家等等分別討論不同的場景,達成共識, 最終從這些場景中抽象出我們的模型。

核心場景(Core Scenario是問題域中的不可以犧牲的問題例項。 犧牲場景,也就是不把某一類場景作為有必要解決的問題, 是一個既需要經驗又需要Vision的難題。我們使用者痛點到底在哪裡,解決這些痛點是否對我們的未來有決定性意義。 舉個例子:亞馬遜的Jeff Bezos認定Mobile不是決定性的, 未來Device是決定性的。 所以有些Mobile shopping場景就不被認為是核心場景。而某些尚未成熟的device shopping場景反倒被認為是核心場景。 這就是一個商業判斷。 當然這個判斷正確與否還有待時間證實。我們做技術的一般不需要做出這樣的判斷, 但是我們必須清晰的瞭解這樣的判斷。 只有這樣我們才能和業務團隊保持同樣的目標。

以上就是我總結的一些常見概念。 其他還有一些概念我們在用到的時候再介紹。  

這是一系列文章的第二篇。 第三篇在此。 


相關文章