# DDD 實現領域驅動設計 ## 第二章:Domains, Subdomains, and Bounded Contexts ### 1.此章重點 此章Domian, Subdomains與Bounded Contexts部份屬於比較概念性的闡述,詳細實做上沒有太多實際的實戰例子,所以此章節只須掌握三個重點。 - a. 理解什麼是Domain, SubDomain還有Core Domain - b. 理解什麼是Bounded Contexts及如何定義 ### 2. DDD軟體設計的策略邏輯 ![](https://i.imgur.com/1E1swW7.png) DDD 並不是要求你的團隊去使用某些 Patterns (event sourcing, repositories patterns),他著重的是如何解構一個複雜的問題,將問題變成數個小的問題,並有效的將其解決。而 DDD 有三大策略邏輯必須要先了解 ![](https://i.imgur.com/Xh5psKu.png) - 一,與各你公司各個領域的專家(Domain Experts)合作定義領域模型(Domain Model)和各個領域的領域詞彙 (Domain Terms)。你如果有涉略DDD的話,你應該會有聽過 ubiquitous language 這個詞,DDD 的目的就是與領域專家合作,為每一個Domain 定義出通用語言(ubiquitous language)。 - 二,利用 Domain Model 和 ubiquitous language 來實作你的系統,不論 class name, method name, event name, variable name,都應該去利用 domain terms 去定義,如此一來,你的產品和工程團隊,和你的商業團隊可以用同樣的詞彙來溝通和討論。 - 三,定義系統的範圍,除非你是從頭開始為一個複雜的商業流程設計一個系統,大部分妳一定需要接觸舊有系統,這些舊系統顯然的不會去遵循你所定義的新詞彙,這是你必須要清楚的劃清系統的範圍 (Boundaries) ,並在新舊系統間,定義一個 Translator / Anti-Corruption Layer,你不需要去對舊有的系統,做全盤的更新,而是利用 Anti-Corruption Layer 去定義系統的範圍和清楚的指出新舊系統之間的對應關係。 ### 2.Domain, SubDomains - Domain: - 整體概括性,例如車用環境景系統, NAS系統... - SubDomain: - 將Domain再細分, 例如車用還景系統的人機操作與影像處理乃至於是否可以配合外部系統例如整合胎壓系統.. --- - Core subdomains: - 核心子領域是你的公司和產品的競爭優勢,也是你商業流程的基礎,在這些子領域持續的投資和創新是你公司成長的基石。你會將你最優秀的團隊成員放在子領域的專案,以期他們能持續的創新。 - EX:環境景系統影像處理。 - Supporting subdomains: - 支援子領域是用來支援核心子領域的系統和使用案例,這些子領域是這個系統不可或缺的一部分,但並不會為你的產品和公司帶來競爭優勢。在軟體系統的投資來說,你會將較資淺的成員放在這類的專案,或是説讓你的資深團隊一次性的建置好此類系統,之後只進行維護的動作。 - EX:環境景系統人機界面。 - Generic subdomains: - 一般子領域是那些你需要的系統功能,但市場上已經有非常多類似的產品,沒有理由去對這部分的功能進行投資,而是需要找到適合的廠商來支援這類子領域的功能。 - EX:環境系統車圖變色 在定義領域模型時,除了軟體架構師和工程師之外,最主要的角色就是那個領域的領域專家(domain expert)了。在這階段,你的團隊會花很多的時間在跟領域專家會談和討論,其目標是要完成下面的事項: - 了解你所需要設計的系統之領域(domain)。 - 了解該領域的使用案例(use cases)。 - 利用領域詞彙(domain terms)去定義通用語言(ubiquitous language)和找出子領域 (subdomains)。 EX : 交通工具租用軟體系統 1. 會員登入 -> 身份驗證( Supporting subdomains) 2. 乘客承租付費-> 付款(Generic subdomains) 3. 發票交易 -> 金融交易(Generic subdomains) 4. 客戶從可用車輛列表中預訂車輛->車輛Booking (Core subdomains) ### 3.Bounded Contexts Domain Driven Design 的設計重點圍繞在Bounded Contexts,Bounded context的想法就是不要把一整個大系統視為一個Context,而是依據「定義方式」把整個大系統分成若干個Bounded context(有界限的context) ![](https://i.imgur.com/tHciehb.png) #### 如何辨識 Bounded Context 通常識別 Bounded Context 會由兩點下手:語意(Linguistic)與業務能力(Business Capability)。 - 1.用語言定義的差別來識別 在 Bounded Context 中我們可以用 Ubiquitous Language 來命名某個概念的實體,而實體程式碼的資料與行為也必須反映出 Ubiquitous Language。 因此 Bounded Context 本質上是一個語意的邊界。同樣是 Account,在銀行、部落格、社交軟體中都有截然不同的定義。當你需要界定一個實體的不同定義時,那你可能需要探索新的 Bounded Context。 總結以下三點: 1.注意那些實體的定義相似或是經常一起出現的詞彙或句子 2.注意那些擁有相似概念或相同名稱,但是它們在不同語境下有不同的詞彙或句子 3.當團隊發現正在重複地使用其他 Bounded Context 的語言時,請思考其必要性,或許根本不需要新的 Bounded Context。 - 2.注重業務能力勝過資料分類 在設計 Bounded Context 初期常出現一個迷思,那就是要依照「實體」(如顧客、訂單、商品)去決定 Bounded Context。再次強調,DDD 的目的不是為了建立資料庫模型而是為系統的行為建立領域模型。如此一來,才能夠讓建立出來的模型表達出系統的領域知識。 甚至還有一派主張 Bounded Context 推薦用動詞做命名來描述 Bounded Context 的行為。 很多專案一開始使用資料表(Table)的資料為依據做系統設計、甚至把業務邏輯與 ORM 框架綁在一起。這麼一來容易造成物件乘載太多的責任,比如說「顧客」是屬於「會員管理系統」還是屬於「購物系統」? 其實同樣是顧客,使用情境卻大不相同,如果單純用資料表為根據定義他,最後一定會長出一個肥大且難用的物件,且無法很好的滿足 Bounded Context 內的一致性。 舉個例子,依照業務能力 (business capabilities)我們需要有會員管理、商品目錄、訂單、物流、金流等等功能。而顧客的概念在不同 Bounded Context 中有不同的行為、職責甚至是不同的名稱,如下: - [會員管理 Bounded Context] 會員 Member - [瀏覽產品目錄 Bounded Context] 瀏覽者 viewer - [訂單 Bounded Context] 買家 Buyer - [物流 Bounded Context] 收貨人 Receiver - [金流 Bounded Context] 付款人 Payer 拆分 Bounded Context 時,很常會把同一實體概念拆分在不同功能的 Bounded Context 中。 #### 對於資料的掌控度與依賴性 通常好的系統會要求保有 Autonomy (自治性),意即,在相當程度上減少彼此的依賴。這個依賴不僅是邏輯上的依賴,同時也包含底層如資料存儲的依賴。 理想上,我們會希望每個系統擁有自己的資料庫,如此一來就能夠保證不被外部污染。 一個 Bounded Context 必須具有自己資料庫的完整擁有權。當一個資料庫不完全屬於這個 Bounded Context 時,必須考慮拆分出去。 譬如一開始一個單純的 購物 Bounded Context 僅需要讓使用者登入並購物,但之後加入了會員等級與關係制度:會員可以升級成 VIP 、VVIP ...等等,甚至可以升級成聯盟行銷夥伴。此時的 購物 Bounded Context 顯然就已經不適合了。因此,可以考慮切出出一個 身份管理 Bounded Context 來管理這些實體。當 購物 Bounded Context 需要相關權限驗證時,僅需和身份管理 Bounded Context 溝通即可。 #### Bounded Context 的大小 Bounded Context 沒有一定的大小,主要還是以語意與業務能力做判斷。在專案啟動時,商業邏輯相對簡單,通常不用太多 Bounded Context 就可以完成大部分的需求,且不破壞內部領域知識的一致性。 雖然沒有一定的大小,但也要小心不要在專案成長過程中讓外部概念滲透進去 Bounded Context 中。這尤其容易發生在整合外部系統(ex: 金物流) 時,團隊尚未消化完外部的知識就直接放進去某個 Bounded Context ,這導致日後團隊開發時,需要看多份文件(別人的也要看!)確認需求異動是否有額外的風險。 #### Bounded Context 常見迷思 - 迷思一:一次就分析到位 事實上大多數系統一開始可能只會有一個 Bounded Context 或是業務複雜度不足以分出多個 Bounded Context 。 - 迷思二:根據技術、分層架構拆分 Bounded Context 比如說 Database、Infrastructure 分為一個 Bounded Context 就不太適合。 或是依照架構層去分如 Use Case 層、 Controller 層、 Domain 層或是外部框架...等等,都不適合。 - 迷思三:根據開發任務拆分 Bounded Context 今天假如有一個新的功能需求進來,不代表他就可以獨立開一個 Bounded Context 出來。 雖然有些人主張 Bounded Context 越小越敏捷(?),但是當 Bounded Context 語意的一致性被打破時,就是留下技術債待未來解決。 - 迷思四:Bounded Context 與後端/技術團隊組織無關 By Conway Law: 軟體組織的團隊邊界決定系統邊界。 Bounded Context 也是一個決定團隊邊界的好方式,甚至可以應用到跨國團隊上。 --- - **同一Domain Name,不同含意** 在產品支援的領域來看,你的銷售代表或是終端顧客都可以是產品支援的顧客;而在銷售的領域來說,顧客的意義就不一樣了。 ![](https://i.imgur.com/EzMgeWx.png) - **同一Domain Name與Definition,但不同使用案例** 下圖為例,Banking Account 可代表存款戶頭,也可代表你在報銷時,所連結的銀行帳戶。在報銷的領域中,系統所需知道的是系統須匯款到哪一個戶頭;而在存款的領域中,存款餘額和存提款的明細是其系統的責任,而存款系統並不需要知道任何報銷系統的資訊。若沒有清楚的定義出使用案例和了解各個詞彙在不同使用案例所代表的意義,在此例中,很容易地會把報銷和存款領域合併,而讓 Banking Account 變成非常複雜的 Model。 ![](https://i.imgur.com/njUmiH3.png) - **外部系統** 外部系統可以是你團隊所維護的 Legacy System 或是你的團隊在產品策略討論後,決定要使用的某個第三方系統來滿足 Generic Domains 的需求。在決定要使用哪一個第三方服務時,你應該要清楚的定義出你所需要該服務所提供的功能,並用 Bounded Buy 的策略 —— 只選擇支援模組化和decoupled的服務。 這些外部系統多半都已經有自己所定義的系統詞彙,根據 DDD 的定義,這些外部系統都應被視作為獨立的 Bounded Context。而其他的Bounded Contexts 會透過 ACL (Anti-Corruption Layer) 去與這些外部系統做溝通。 ### 4.Bounded Sample ![](https://i.imgur.com/3Va6HhA.png) ![](https://i.imgur.com/Xbv3kgT.png) ![](https://i.imgur.com/XV8eiXF.png)