DDD中限界上下文與通用語言的作用

等不到的口琴發表於2021-06-06

什麼是通用語言

通用語言, 最主要的目的就是減少交流中資訊丟失, 在實際開發中, 可能關聯很多人, 例如有業務層面的業務細節制定者、領域專家、產品經理、專案經理 、架構師、開發經理、測試經理等等, 即使確定了核心域, 但是對於同樣的領域知識, 每個人也有自己的理解, 舉個例子, 我們通常說的商品和貨物,是不是其實就是一個東西 ? 在銷售領域叫商品, 但是一旦進入物流領域就叫貨物, 所以我們一說商品就知道這是在銷售子域, 一旦說貨物就知道這是在物流領域, 如果只用物品來進行交流可能就會丟失資訊,因為在不同的子域關注的重點是不同的, 為了減少資訊丟失, 確保大家交流的便利性, 將能夠簡單清晰準確描述業務含義和規則的語言就是領域通用語言, 也就是通用語言是團隊統一的語言, 只要你在這個領域, 那麼在一個系統生命週期內就應該用通用語言進行交流。

可見, 通用語言作用還是很清晰明瞭的, 解決溝通障礙節省時間成本, 讓大家更好進行協作, 通用語言包含術語與應用場景,並且能夠反映在程式碼中, 例如給領域物件命名, 如商品、訂單等,對應實體物件; 而動詞則表示一個動作或者事件, 例如商品已經下單, 訂單已付款, 運輸中等等, 對應領域事件或者說命令。

通用語言貫穿 DDD 的整個設計過程。作為專案團隊溝通和協商形成的統一語言,基於它,你就能夠開發出可讀性更好的程式碼,將業務需求準確轉化為程式碼設計。

  1. 在事件風暴的過程中,領域專家會和設計、開發人員一起建立領域模型,在領域建模的
    過程中會形成通用的業務術語和使用者故事。事件風暴也是一個專案團隊統一語言的過
    程。
  2. 通過使用者故事分析會形成一個個的領域物件,這些領域物件對應領域模型的業務物件,
    每一個業務物件和領域物件都有通用的名詞術語,並且一一對映。
  3. 微服務程式碼模型來源於領域模型,每個程式碼模型的程式碼物件跟領域物件一一對應。

設計過程中我們可以用一些表格,來記錄事件風暴和微服務設計過程中產生的領域物件及其屬性。比如,領域物件在 DDD 分層架構中的位置、屬性、依賴關係以及與程式碼模型物件的對映關係等。下面是一個微服務設計例項的部分資料,表格中的這些名詞術語就是專案團隊在事件風暴過程中達成一致、可用於團隊內部交流的通用語言。在這個表格裡面我們可以看到,DDD 分析過程中所有的領域物件以及它們的屬性都被記錄下來了,除了 DDD 的領域物件,我們還記錄了在微服務設計過程中領域物件所對應的程式碼物件,並將它們一一對映。

DDD 分析和設計過程中的每一個環節都需要保證限界上下文內術語的統一,在程式碼模型設計的時侯就要建立領域物件和程式碼物件的一一對映,從而保證業務模型和程式碼模型的一致,實現業務語言與程式碼語言的統一。

如果做到了這一點,也就是建立了領域物件和程式碼物件的對映關係,那就可以指導軟體開發人員準確無誤地按照設計文件完成微服務開發了。即使是不熟悉程式碼的業務人員,也可以很快找到程式碼的位置。

什麼是限界上下文

剛剛有聊到通用語言, 而確定通用語言適用範圍, 準確來說, 確定領域範圍的就是限界上下文邊界, 限界上下文邊界英文名bounded context, 如果直接翻譯成上下文邊界就更容易理解, 主要目的是為了避免同樣的概念在不同領域產生不同語義或歧義, DDD在戰略上提出"限界上下文"這個概念, 用來確定語義所在的領域邊界。

我們可以將限界上下文拆解成兩個詞語:限界和上下文。 限界就是領域的邊界, 而上下文就是語義環境, 通過限界上下文讓所有交流的人知道我們聊的是在同一個領域邊界內的事情, 合起來就是用來封裝通用語言和領域物件,提供上下文環境,保證在領域之內的一些術語、業務相關物件等(通用語言)有一個確切的含義,沒有二義性。這個邊界定義了模型的適用範圍,使團隊所有成員能夠明確地知道什麼應該在模型中實現,什麼不應該在模型中實現。

為什麼要有限界上下文這個概念

都說中文這門語言非常豐富,在不同的時空和背景下,同樣的一句話會有不同的涵義。有一個例子你應該聽說過。在一個明媚的早晨,孩子起床問媽媽:“今天應該穿幾件衣服呀?”媽媽回答:“能穿多少就穿多少!”

那到底是穿多還是穿少呢?

如果沒有具體的語義環境,還真不太好理解。但是,如果你已經知道了這句話的語義環境,比如是寒冬臘月或者是炎炎夏日,那理解這句話的涵義就會很容易了。所以語言離不開它的語義環境。而業務的通用語言就有它的業務邊界,我們不大可能用一個簡單的術語沒有歧義地去描述一個複雜的業務領域。限界上下文就是用來細分領域,從而定義通用語言所在的邊界。現在我們用一個保險領域的例子來說明下術語的邊界。

保險業務領域有投保單、核保、財務、回訪、保全等保險術語,它們分別應用於保險的不同業務流程。

  1. 客戶投保時,業務人員記錄投保資訊,系統對應有投保單實體物件。
  2. 繳費完成後,業務人員將投保單轉為保單,系統對應有保單實體物件,保單實體與投保
    單實體關聯。
  3. 如客戶需要修改保單資訊,保單變為批單,有保全系統對應有批單實體物件,批單實體與保單
    實體關聯。
  4. 如果客戶發生理賠,生成賠案,系統對應有報案實體物件,報案實體物件與保單或者批單實體關聯。投保單、保單、批單、賠案等,這些術語雖然都跟保單有關,但不能將保單這個術語作用在保險全業務領域。因為術語有它的邊界,超出了邊界理解上就會出現問題。
  5. 正如電商領域的商品一樣,商品在不同的階段有不同的術語,在銷售階段是商品,而在運輸階段則變成了貨物。同樣的一個東西,由於業務領域的不同,賦予了這些術語不同的涵義和職責邊界,這個邊界就可能會成為未來微服務設計的邊界。看到這,我想你應該非常清楚了,領域邊界就是通過限界上下文來定義的。

限界上下文在微服務設計中作用以及意義是什麼

接下來,我們對這個概念做進一步的延伸。看看限界上下文和微服務具體存在怎樣的關係。我想你買過保險吧,或者聽過吧。保險承保的流程包含了投保、繳費、出單等幾個主要流程。如果出險了還會有報案、查勘、定損、理算等理賠流程。

首先,領域可以拆分為多個子領域。一個領域相當於一個問題域,領域拆分為子域的過程就是大問題拆分為小問題的過程。在這個圖裡面保險領域被拆分為:投保、支付、保單管理和理賠四個子域。

子域還可根據需要進一步拆分為子子域,比如,支付子域可繼續拆分為收款和付款子子域。拆到一定程度後,有些子子域的領域邊界就可能變成限界上下文的邊界了。子域可能會包含多個限界上下文,如理賠子域就包括報案、查勘和定損等多個限界上下文(限界上下文與理賠的子子域領域邊界重合)。也有可能子域本身的邊界就是限界上下文邊界,如投保子域。

每個領域模型都有它對應的限界上下文,團隊在限界上下文內用通用語言交流。領域內所有限界上下文的領域模型構成整個領域的領域模型。理論上限界上下文就是微服務的邊界。我們將限界上下文內的領域模型對映到微服務,就完成了從問題域到軟體的解決方案。

可以說,限界上下文是微服務設計和拆分的主要依據。在領域模型中,如果不考慮技術異構、團隊溝通等其它外部因素,一個限界上下文理論上就可以設計為一個微服務。不過,這裡還是要提示一下:除了理論,微服務的拆分還是有很多限制因素的,在設計中不宜過度拆分。那這個度怎麼把握好呢?有關微服務設計和具體的拆分方法,我會在實戰篇中詳細講解。

總結

通用語言確定了專案團隊內部交流的統一語言,而這個語言所在的語義環境則是由限界上下文來限定的,以確保語義的唯一性。而領域專家、架構師和開發人員的主要工作就是通過事件風暴來劃分限界上下文。限界上下文確定了微服務的設計和拆分方向,是微服務設計和拆分的主要依據。如果不考慮技術異構、團隊溝通等其它外部因素,一個限界上下文理論上就可以設計為一個微服務。可以說,限界上下文在微服務設計中具有很重要的意義,如果限界上下文的方向偏離,那微服務的設計結果也就可想而知了。因此,我們只有理解了限界上下文的真正涵義以及它在微服務設計中的作用,才能真正發揮 DDD 的價值,這是基礎也是前提。

相關文章