- 業務建模
- 解決問題還是定義問題
- 業務建模的難點
- 如何定義問題并讓所有人接受
- 如何在特定架構下實作模型
- 學習業務建模的建議
- 領域驅動設計
- 領域模型對于業務系統是更好的選擇
- 知識消化
- 知識消化的五個步驟
- 模型與軟體實作關聯
- 從貧血模型到富含知識的模型
- 通過聚合關系表達業務概念
- 修改模型就是修改代碼
- 統一語言是必要的嗎
- 統一語言是基于領域模型的共同語言
- 修改代碼就是改變統一語言
- 一個簡單的統一語言提案
- 如何理解DDD
- 將提煉知識的回圈看做開發流程
- 示例
- 研發方與業務方的協同效應
- 當討論DDD時,我們到底在說什么
- 迭代式試錯建模法
- 具有協同效應的作業方式
- 價值觀體系
- DDD的特點
- 將提煉知識的回圈看做開發流程

業務建模
解決問題還是定義問題
業務建模首先是一個定義問題的方法,其次才是解決問題的方法,我們很容易理解解決問題帶來的價值,但也很容易忽略定義問題的力量,如果問題定義得準確,那么實作起來也不會太復雜;相反,如果沒有搞清楚要解決什么問題,就可能需要用各種奇技淫巧去彌補問題定義的不足,我們有時為了逃避真正的思考,愿意做任何事,
為了有效定義問題,需要從業務出發,首先嘗試在業務中尋找簡化問題的可能性,然后在技術中尋找對應的解決方案,
明確業務中的關鍵問題,使用易于實作的模型將業務問題表達出來,
業務建模的難點
而一旦涉及軟體開發的核心難點,也就是處理隱藏在業務知識中的核心復雜度,除了清晰得理解業務訴求之外,還需要通過建模的方式對這種復雜度進行簡化與精煉,
業務建模的方法有很多種,比如著眼于資料庫設計的物體關系法(E-R Modeling)、面向物件分析與設計法(Object Oriented Analysis and Design),圍繞知識消化的領域驅動設計(Domain Driven Design)等等,
業務建模的真正難點并不在于建模本身,而是:
- 清晰地定義業務問題,并讓所有干系人都接受你對業務問題的定義;
- 在特定架構的約束下,將模型實作出來,
如何定義問題并讓所有人接受
這里的定義業務問題,是指對業務問題的梳理和總結,明確對業務的影響及產出,
這就需要對業務進行提煉總結,并通過所選用的業務建模方法中蘊含的邏輯框架去驗證它,如果發現漏洞和不足,要及時提出,讓人參與討論,
這里的挑戰不是建模本身,而是如何獲取業務方的信任,并展開有效的討論,
這是能否有效使用業務建模方法的關鍵,
如何在特定架構下實作模型
建模方法有著更長的生命周期,而技術架構卻在不斷演化,如果忽略架構對模型的影響,往往會因為不知道如何處理架構約束,而無法將其運用到實際作業中,
學習業務建模的建議
- 轉移關注點,不必太在意模型是否完美,是否在概念上足夠抽象,是否使用了模式,反而,更應該關注如何圍繞模型,建立有效的溝通、反饋機制,即該怎么將模型中蘊含的邏輯講給別人,并讓別人聽懂、并給出反饋,
- 對架構演化趨勢保持足夠的關注度,一般每3-5年就會出現新的架構風格,過去15年經歷了從單體到多層,再到微服務的改變,在不同的架構風格下,業務建模和模型實作模式的最佳實踐會存在差異,而這些差異很可能會決定建模的成敗,
領域驅動設計
說起業務建模,領域驅動設計(Domain Driven Design)是一個繞不開的話題,
軟體開發的核心難點在于處理隱藏在業務知識中的復雜度,那么模型就是對這種復雜度的簡化與精煉,領域驅動設計是一種模型驅動的設計方法,通過領域模型(Domain Model)捕捉領域知識,使用領域模型構造更易維護的軟體,
模型在領域驅動設計中有三個用途:
- 通過模型反映軟體實作的結構;
- 以模型為基礎形成團隊的統一語言(Ubiquitous Language);
- 把模型作為精粹的知識,以用于傳遞;
這樣的好處:
- 理解了模型,就大致理解了代碼的結構;
- 在討論需求時,研發人員可以很容易明白需要改動的代碼,并對風險與進度有更好的評估;
- 模型比代碼更簡潔、更抽象,有更低的傳遞成本;
領域模型對于業務系統是更好的選擇
“程式=演算法+資料結構”這個著名的公式來自于軟體行業早期,當時堆、堆疊、鏈表等與領域無關的模型,幫開發人員解決了編譯器、記憶體管理、索引等大量的基礎問題,這讓從業人員形成了一種習慣:將問題轉化為與具體領域無關的資料結構,即構造與具體領域無關的模型,
而領域驅動設計則是對這種習慣的挑戰,它提倡對于業務軟體而言,從業務出發去構造與業務強相關的模型,是一種更好的選擇,
因為如果我們構造的是業務系統,那么團隊中就會引入不具有開發背景的業務方參與;這種情況下,與領域無關的資料結構及演算法,業務方是不了解的;這種認知上的差異,會造成團隊溝通的困難,破壞統一語言,加劇知識傳遞的難度,
于是在業務系統中,構造一種專用的模型(領域模型),將相關的業務流程與功能轉化為模型的行為,就能避免開發人員與業務的認知差異,所以領域模型對于業務系統是更好的選擇,
這種理念的轉變是以面向物件技術開始,直到DDD被行業采納,才最終完成的,
知識消化
不同于軟體行業對資料結構的嘗試研究與積累,在不同的領域中該使用什么樣的領域模型,并沒有一個現成的做法,因而在DDD中,Eric Evans提倡了一種叫知識消化(Knowledge Crunching)的方法幫助我們去提煉領域模型,多年來產生了多種知識消化的方法,但它們在宏觀上仍然遵從知識消化的五個步驟,
知識消化的五個步驟
- 關聯模型與實作;
- 基于模型提取統一語言;
- 開發富含知識的模型;
- 精煉模型;
- 頭腦風暴與試驗;
其中“關聯模型與實作”是知識消化可以順利進行的前提與基礎,它將模型與代碼統一在一起,使得對模型的修改,等同于對代碼的修改,
“基于模型提取統一語言”則會將業務方變成模型的使用者,通過統一語言進行需求討論,實際上就是通過模型對需求進行討論,
后面三步則構成了一個提煉知識的回圈:通過統一語言討論需求;發現模型中的缺失或者不恰當的概念,精煉模型以反映業務的實踐情況;對模型的修改引發了統一語言的改變,再以試驗和頭腦風暴的態度,使用新的語言以驗證模型的準確,如此回圈往復,不斷完善模型與統一語言:

以上五步可以總結為“兩關聯,一回圈”:
- 模型與軟體實作關聯;統一語言與模型關聯;
- 提煉知識的回圈;
模型與軟體實作關聯
與模型關聯的實作方法,是一種面向物件的編程風格,即“富含知識的模型”(Knowledge Rich Model),與之相對的,程序式的編程風格,則是一種與模型無關的實作方式,面向物件物件技術在表達領域模型上有天生的優勢,
從貧血模型到富含知識的模型
在“貧血物件模型”中,物件僅僅對簡單的資料進行封裝,而關聯關系和業務計算都散落在物件的范圍之外,
比如極客時間的用戶和訂閱專欄是一對多的關系,“貧血物件模型”類似下面的偽代碼:
class UserDAO{
public User find(long id){
var query = connection.createConnection();
ResultSet result = query.executeQuery();
return new User(rs.getLong(1),rs.getString(2));
}
}
class SubscriptionDAO{
public List<Subscription> findSubscriptionsByUserId(long userId){
...
}
}
這樣的代碼風格沿用程序式方式,沒有發揮面向物件技術的優勢,
與之相對的則是“充血模型”,即“與某個概念相關的主要行為與邏輯,都被封裝到了對應的領域物件中”,這也就是DDD中強調的“富含知識的模型”,按照這種風格,上面的代碼可以改寫為:
class User{
public List<Subscription> getSubscriptions(){
...
}
}
class UserRepository{
public User findById(long id){
}
}
從這段代碼很容易看出:User是聚合根,Subscription被聚合到User中,無法獨立存在,
通過聚合關系表達業務概念
在建模中采用的關聯關系中,聚合關系表示關聯在一起的物件,從概念上講是一個整體,在User與Subscription的關系中,如果脫離了Subscription,Use只是單純的表示個人資訊,而脫離了User,Subscription也只是專欄內容,只有把兩者放在放在一起,才能表達需要的含義:User訂閱的Subscription,它們是一個整體,
比起貧血模型的實作方式,充血模型將模型與軟體實作完全對應在一起的,無論是結構還是行為,這簡化了理解代碼的難度,只要在概念上理解了模型,就會大致理解代碼的實作方法與結構,
修改模型就是修改代碼
關聯模型與軟體實作,最終的目的是:修改模型就是修改代碼;修改代碼就是修改模型,
在知識消化中,提煉知識的重構是圍繞模型展開的,如果對于模型的修改,無法直接映射到軟的實作上(比如貧血模型),那么提煉知識的重構回圈就必須停下來,等待這個同步的程序;否則模型與軟體將的割裂,就會將模型本身分裂為更接近業務的分析模型,以及更接近實作的設計模型,這個時候分析模型就會逐漸退化為純粹的溝通需求的工具,而一旦脫離了實作的約束,分析模型就會變的天馬行空,不著邊際,

這套做法被無數案例證明難以成功,于是才有了后來的DDD等使用統一模型的方法,
統一語言是必要的嗎
DDD的核心理念,即在業務系統中使用與問題域相關的模型,而不是通用的資料結構去描述問題,
既然領域驅動設計是一種模型驅動的設計方法,為什么不能讓業務方直接去使用模型,而要通過統一語言呢?
統一語言是基于領域模型的共同語言
統一語言(Ubiquitous Language)是基于領域模型的共同語言,業務方與技術方通過共同語言描述業務規則與需求變動,為雙方提供了協作、溝通的基礎,這里的業務方泛指一切非最終軟體實作者,比如客戶、產品經理、業務分析師、解決方案架構師、用戶體驗設計師等,
共同語言也有很多種形式,如用戶畫像、用戶旅程、資料字典等;但DDD的統一語言特指根據領域模型構造的共同語言,
回到開始的問題:“既然領域驅動設計是一種模型驅動的設計方法,為什么不能讓業務方直接去使用模型,而要通過統一語言呢?”
這是因為這樣做的效果不理想:
- 對于研發人員來說,直接使用模型有很多好處,但對業務方來說卻不夠直觀,業務方大多習慣從業務角度如流程、互動、功能、價值等去描述系統,而模型則偏重于資料角度,描述了在不同業務維度下,資料將會如何變化,以及如何支撐對應的計算與統計,業務維度被模型的抽象隱藏了,使業務方無法從模型中直接感知到業務維度,
- 模型是從已知需求中總結提煉的知識,這就意味著模型無法表達未知需求中尚未提煉的知識,所以需要一個相對允許歧義與未知的隔離層,來幫助我們發現和反饋模型的不足,
- 總結來說,需要一種能與模型關聯的共同語言,它既能讓模型在核心位置扮演關鍵角色,又能彌合視角差異,并提供足夠的緩沖,從模型中提取的統一語言,覆寫了領域模型中的概念與邏輯,還提供了必要的補充,以幫助業務方理解模型,同時,統一語言也扮演了試驗田的角色,其中出現的未被提取的知識,將觸發提煉知識的回圈,逐步完善模型,
相對于模型的精確,統一語言的模糊反而更能滿足人與人之間交流的需求,
修改代碼就是改變統一語言
統一語言是從模型中提取的,于是,修改代碼就是修改模型,也會改變統一語言,所以,不僅是因為需求變更,代碼重組引起的代碼修改,最終也會反映到統一語言中,
這相當于讓開發方來定義業務問題,并要求業務方按照開發提出的模型來描述業務和需求,一旦將軟體實作與領域模型關聯,那么對實作的簡化,也就是對領域問題的簡化;從實作中抽取的抽象概念,也就是從問題域中抽取的抽象概念,
這種看起來匪夷所思的情況,實際上一直都在發生,技術方一直都在定義業務,只是沒有合理的途徑讓業務方了解并接納,
統一語言及其背后的領域模型從觀念上改變了這一狀況,它將大家從各自的領地,也就是業務與技術中,拉到了一個中間地帶,雙方有了差不多的話語權,
而一旦業務方接受了統一語言,實際上就是放棄了對業務100%的控制權,也就意味著統一語言在業務上能夠賦予開發人員更大的控制權,賦予了開發人員定義業務的權力,不多在帶來權力的同時,也隱含著額外的義務,即借由統一語言,在提煉知識的回圈中,接受業務方的監督與反饋;如果業務方不同意技術方修改的模型,可以修正這種誤差,也就是技術人員也喪失了對代碼100%的控制權,
總結來說,統一語言提供了一種更好的協同方式的可能性,建立了技術反饋業務的途徑,降低了知識消化程序失敗的風險,
一個簡單的統一語言提案
統一語言本身的形式并不重要,或者說并沒有統一的形式,重要的是,統一語言與領域模型關聯、且多方認可并承認對其的集體所有權時,統一語言才能成為真正的統一語言,在此之前,都只是統一語言提案,
統一語言可以包含以下內容:
- 源自領域模型的概念與邏輯
- 界限背景關系(Bounded Context)
- 系統隱喻
- 職責的分層
- 模式與慣用法
以極客時間專欄中User與Subscription的模型為例,可以是:

這個模型非常簡單,無法包含上述所有的內容,但根據這個模型,可以從中提取到對應的領域概念,即領域模型中的物體:
- 用戶(User),是指所有在極客時間注冊過的人;
- 訂閱的專欄(Subscription),是指用戶付費過的專欄;
同時還有一個業務邏輯:
- 用戶可以訂閱多個專欄
界限背景關系、系統隱喻等其他幾項都可以看作對業務維度的補充與展開,將它們引入通過統一語言后,可以幫助業務方更直觀地理解模型:
- 系統隱喻就是在價值與業務模式維度上的補充與擴展,比如“某某領域的淘寶”這樣的說法,言簡意賅地表示了產品的愿景、價值定位、核心模式;
- 職責分層,關注穩定性,哪些是穩定的哪些是易變的;
- 模式與慣用法是業務規則、流程與實作模式;
- 界限背景關系是圍繞某些模型設定的邊界,所有人對于如何對于如何利用邊界中的模型有清晰明確的想法,這個想法借由這個邊界保持一致,不受外界資訊干擾,界限背景關系是現實中某種決定在模型上的反應,這里存在訂閱子域,訂閱也是一個界限背景關系,
綜上,抽取的語言是:
- 用戶(User)
- 訂閱的專欄(Subscription)
- 用戶可以訂閱多個專欄
- 訂閱
統一語言是在使用中被確立的,通過定義與解釋,我們使這些詞語在其所使用的是下文中沒有歧義,再通過這些基礎詞匯,來描述業務中的行為或規則,慢慢就可以將其確立為統一語言了,
這個語言可以用來撰寫用戶需求:
作為一個User,當我查閱購買過的Subscription時,可以看到其中的教學內容,
可以用來描述行為驅動開發的測驗:
當User已購買過某個Subscription,那么當其訪問時,就不需要再為內容付費,
或是實體化需求的說明等等,重點是要在所有可能的地方使用這個語言,只有當所有工種角色都接受它,使用它去描述業務和系統的時候,它才會真正成為統一語言,
領域驅動設計,逼著業務方和實作方像玩蹺蹺板一樣共同構建業務系統,從協作角度看,這個游戲的特點是,【一個人沒法玩】, 板≈共同語言,支點≈模型,以“支點”為中心,以“板”為媒介,雙方你一下我一下地玩,“板”+“支點”很大程度上定義了游戲的基本規則,
如何理解DDD
從技術方的角度來說,統一語言和它背后的領域模型,賦予了研發人員通過重構定義業務的能力,那么從業務方的角度來看,如何利用統一語言去影響研發方呢?這就要涉及到“兩關聯一回圈”中提煉知識的回圈了,它是DDD的核心流程,
將提煉知識的回圈看做開發流程
提煉知識的回圈大致為:
- 首先,通過統一語言討論需求;
- 而后,發現模型中的缺失或者不恰當的概念,然后精煉模型,以反映業務的實際情況;
- 接著,對模型的修改會引發統一語言的改變,再以試驗和頭腦風暴的態度,使用新的語言以驗證模型的準確性,
這與重構的程序類似:
1. 發現壞味道以明確改進方向
2. 嘗試消除味道
3. 通過壞味道是否消失判斷改進是否成功
也與TDD的程序類似:
1. 構造失敗測驗表明需求變化
2. 修改代碼實作
3. 通過測驗以證明滿足需求
提煉知識的回圈可以看做以模型為最終產出物的研發流程;如果是重構,業務方就是發現壞味道的人,如果是TDD,業務方就是構造測驗的人,
示例
之前極客時間的例子里,抽取的統一語言是:
- 用戶(User)
- 訂閱的專欄(Subscription)
- 用戶可以訂閱多個專欄
- 訂閱
并用這個語言描述了需求:
作為一個User,當我查閱購買過的Subscription時,可以看到其中的教學內容,
當User已購買過某個Subscription,那么當其訪問時,就不需要再為內容付費,
上述語言中只存在購買過的專欄,但如果業務方有了這有一個新的需求:
作為一個User,當我對某個專欄的內容感興趣時,我可以購買這個專欄,使其成為我購買過的專欄
這條業務描述中出現了之前的語言無法表述的概念:未購買的專欄、內容;這意味著當前的統一語言存在無法表述的業務,模型存在漏洞,為了彌補漏洞,團隊可以頭腦風暴,提取缺失的概念,修改已有的模型,并由業務方判斷修改后的模型是不是足夠準確表達了業務概念與邏輯,最后可能得到這樣的新模型:

重新提取統一語言:
- 用戶(User),極客時間注冊過的人
- 付費內容(Content)是課程的承載,可以是文字、視頻、音頻
- 作者(Author)付費內容的創作者
- 專欄(Column)是一組付費內容(Content)的集合,由極客時間的作者(Author)提供
- 訂閱的專欄(Subscription),用戶付費過的專欄
- 用戶可以訂閱多個專欄
- 專欄中可以包含多個付費內容
- 同一作者可以發布多個專欄
- 訂閱
- 課程發布
于是之前無法被描述的業務邏輯變成了:
作為一個User,當我對某個專欄(Column)的內容(Content)感興趣時,我可以購買這個專欄,使其成為我購買過的專欄(Subscription)
研發方與業務方的協同效應
提煉知識的回圈可以看做以模型為最終產出物的研發流程;另外,模型與代碼是關聯的,對模型的修改也就是對代碼的修改,如果說統一語言與模型的關聯賦予了技術方定義業務的權力,那么提煉知識的回圈也賦予了業務方影響軟體實作的權利,
綜合來看,業務方與技術方的權利義務有:
- 對于技術方來說,通過統一語言獲得了定義業務的權力,但同時也要承擔在提煉知識的回圈中接受業務影響實作的業務;
- 對于業務方來說,提煉知識的回圈賦予了它們影響軟體實作的權利,同樣需要承擔接受研發方通過統一語言定義業務概念的業務;
可見,知識消化以一種權責明確的方式,讓業務方與技術方參與到對方的作業中,給與了業務方和技術方一種更好的協同方式,
這樣做的好處有:
- 首先,軟體開發的核心難度在于處理隱藏在業務知識中的復雜度,為了處理這種復雜度,需要打破知識壁壘(統一語言),如果雙方對彼此的知識域有基礎的了解(模型),那么知識傳遞與溝通的效率會更高,
- 其次,對于復雜的問題(沒有現成答案的問題),需要快速的反饋周期來試錯(提煉知識的回圈)
當討論DDD時,我們到底在說什么
DDD至少可以指代一種建模法、一種協同作業方式、一種價值觀,以及上述三種按照隨意比例的混合,
迭代式試錯建模法
針對同一個問題,不同人得到的模型可能是不一樣的,模型是對問題的抽象,沒有對錯,只有角度不同,正如DDD提出者Eric Evans所說:“知識消化是一個探索的程序,你不可能知道將會在哪里停止”,言外之意“你可能不知道當你停止時,得到的是垃圾還是寶藏”,這只能交給建模者的抽象能力了,
DDD之所以會變成如此不靠譜的建模法,是因為它嘗試解決的問題是復雜問題,即沒有現成答案的問題,那么迭代式試錯法就是唯一可行的方法,
具有協同效應的作業方式
權利與義務的對等,構成了協同的基礎:
- 對于技術方來說,通過統一語言獲得了定義業務的權力,但同時也要承擔在提煉知識的回圈中接受業務影響實作的業務;
- 對于業務方來說,提煉知識的回圈賦予了它們游戲軟體實作的權利,同樣需要承擔接受研發方通過統一語言定義業務概念的業務;
不過這種協同方式是否能發揮作用,很大程度上依賴與團隊對模型、對統一語言的理解與接納,需要團隊逐步在作業中使用它,這依賴于建模者的變革管理能力,而知識消化希望通過頭腦風暴與試驗的方法,簡化這種變革,實際結果仍然因人而異,
價值觀體系
DDD背后的價值觀為:
- DDD是一種模型驅動的設計方法,模型應處在核心;
- 兩關聯一回圈:業務與技術圍繞著模型的協調
從這個角度來說,DDD的定義太過寬泛,以至于可以使用DDD去討論任何事情,所以更建議用“知識消化”來代替DDD,因為“知識消化“除了上述兩條外,還框定了具體的實踐架構:如何更好地構建富含知識的模型、如何保持模型與軟體實作的關聯、如何有效地提取領域語言、如何推動業務方更主動地參與提煉知識的回圈,
DDD的特點
- DDD作為建模法,是一種迭代試錯法,是一種保底可行,但效率不高的方法;如果技術方與業務方沒有足夠的信任,可能就迭代不起來了;
- DDD作為一種協同作業方式,提供了相當精彩的思路,統一語言的概念對行業產生了深遠的影響;
- 統一語言并不容易實作,而一旦無法形成真正的統一語言,提煉知識的回圈也就無法進行,這個方法也就失敗了;
- DDD更適合敏捷團隊,因為無論是通過統一語言協同互動、還是提煉知識的回圈,都需要對這種跨工種協同以及漸進式改進方法有足夠的認同,對于敏捷團隊方法實施較好的團隊,這些都不是問題,否則會有較大的變革成本,
參考資料
極客時間:如何落地業務建模 徐昊
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/413133.html
標籤:其他
下一篇:指紋登錄是怎么跑起來的
