1.DDD的背景和來源
1.1 DDD的核心思想和解決的痛點問題
1.1.1 什麼是模型和建模
模型是對領域的抽象和模擬。
建模是針對特定問題建立領域的合理模型。
1.1.2 軟件系統的複雜性來自哪裏
複雜性來源:
- 業務複雜導致模型複雜
- 技術實現引入額外複雜性
業務複雜性導致模型複雜
技術實現引入額外複雜性
可以引入搜索防腐層:
1.1.3 DDD的關鍵思想以及如何應對多變的複雜性
核心思想一、模型分解
- 領域劃分是面向問題空間的
- 限界上下文是面向解決方案空間的
核心思想二、模型驅動設計(Model Driven Design)
- 通過分層架構隔離領域層、仔細選擇模型和設計方案等措施保持實現與模型的一致。
1.2 面向對象和敏捷與DDD的區別與聯繫
面向對象分析與設計沒法應對大型複雜系統。
敏捷開發流程:
1.3 DDD的過去、現在與未來
應用
- 產業互聯網
- 企業數字化轉型
理論和工具
- 更多建模方法和工具
- 與大數據和AI的結合
2.戰略設計
2.1 案例背景介紹
面向自動售賣機的零售SaaS
團隊背景
- A公司:SaaS公司,主要面向零售企業客戶
- SmartRM產品團隊:一位資深產品經理,幾位產品策劃
- SmartRM研發團隊:一位架構師,10+開發,資深開發3至4位
客戶背景
- B集團,零售行業某頭部商家
- 全國各地有大量商超和便利店,有成熟的內部系統和供應鏈
- 基於自動售賣機的零售業務是新業務
2.2 建模和設計的整體流程
整體流程(重疊的,建模渦流):
- 挖掘用戶故事
- 建立通用語言
- 戰略設計
- 戰術設計
挖掘用戶故事
- 問題空間的描述
- 文字表述
- 討論 + 圖形表達
建立通用語言
- 在討論模型和定義模型時,團隊使用的同一種語言
- 領域知識需要在團隊內部高效流轉,模型需要描述
- 通用語言要體現在代碼裏
戰略設計
- DDD中對問題空間和解決方案空間進行分解的過程
- 目的是分解模型以控制複雜性
- 是DDD與傳統建模和設計方法的核心區別之一
戰略設計主要做三件事:
- 領域劃分
- 尋找限界上下文(BC)
- 確定上下文映射
戰術設計
- 對各個BC的細節設計過程
- BC內部的模型結構與完整技術方案
常用的建模方法
- Domain Storytelling 領域故事陳述法
- Event Storming 事件風暴法
- 4C 四色建模法
2.3 如何描述模型?從用戶故事到通用語言
2.3.1 用戶故事
用戶故事
- 不要拍腦袋定需求
- 基於用戶故事討論
什麼是用戶故事?
- 在軟件開發中,用戶故事是一種對軟件系統特性的非正式的自然語言描述,是敏捷軟件開發中從終端用戶的角度對軟件系統特性進行捕捉的一種方式。用戶故事描述了不同類型的用戶需要什麼以及爲什麼需要,它可以幫助我們創建需求的簡單描述。
- 在軟件開發和演進過程中,隨着產品和開發對產品認識的加深,需求總是在不斷變化,所以,過早地進入需求細節以及對細節的描述,是一種時間上的巨大浪費。從這一點來說,用戶故事提供了一種恰到好處的粒度,使得產品在需求分析階段能夠極大地節約時間,並且使產品和研發人員始終把注意力集中在關鍵點,避免他們過早地陷入細節以及被細節所侷限,同時給產品功能留出了討論空間,從而使產品有機會在討論過程中得到優化。
用戶故事的構建一般來說有三個環節:
- 1)簡單描述用戶需求;
- 2)圍繞簡單描述進行討論;
- 3)明確如何驗證。
分別對應用戶故事的三個元素,也就是3C:Card(卡片)、Conversation(談話)、Confirmation(驗證)。
- Card卡片:指對用戶故事的簡述(傳統上人們通過便利貼在白板上構建用戶故事),一個好的用戶故事卡片包括三個要素(誰Who:誰需要這個功能;需要什麼What:想通過系統完成什麼事情;爲什麼Why:爲什麼需要這個功能,這個功能帶來什麼樣的價值)(As a role, I want to action, so that benefit)
- Conversation:談話是指用戶、領域專家、產品經理、研發之間圍繞用戶故事進行的討論,談話是明確需求細節的必要環節。可以用文字對談話進行簡要記錄,此外,也可以基於圖形或其他工具進行討論。
- Confirmation:驗證代表了驗收測試,描述了客戶或者產品owner怎樣確定用戶故事已經被實現,且能夠滿足需求。
Confirmation模板:
假設我是<角色>,在xxx情況下,
當我<操作>,
那麼<結果>。
2.3.2 使用用戶故事收集和梳理SmartRM的需求
- 售賣機掃碼支付購物 售
- 卡片
作爲用戶,
我希望在售賣機上通過手機掃碼支付購買商品,
以便快速便捷地購物。 - 談話
- 驗收標準
假設我是一名用戶,貨道售賣機屏幕的商品列表上有商品A, B, C,
當我在售賣機屏幕上選擇了商品A,並掃描展示的二維碼完成支付後,
那麼商品A就會從售賣機中彈出,我可以拿到商品A。
- 櫃門機免密購物
- 卡片
作爲用戶,
我希望在支持自動結算免密支付的櫃門機上掃碼開門後拿到商品,關門就可以結束購物,系統後臺可以自動結算並且扣費,以便在櫃門機上能更快捷地進行購物。 - 談話
- 驗收標準
假設我是一名用戶,櫃門機裏有商品A, B, C
當我打開櫃門,從櫃門機拿走商品A並且關閉櫃門,
那麼後臺會自動完成結算和支付,從用戶賬戶扣除商品A的價格,並且鎖定櫃門。
其他頂層用戶故事還包括:
- 售賣機投放
- 售賣機撤銷
- 補貨補
- 經營分析
2.3.3 使用Domain Storytelling分析用戶故事
Domain Storytelling是一種領域分析建模方法,通過這種方法,產品和研發人員可以利用語言學習的相同原理建立或者學習一個領域的通用語言,並且建立領域模型。
在storytelling過程中,一方(通常是架構師)聆聽另一方(通常是產品經理或者領域專家)以主謂賓的句型(誰做了什麼)講述用戶故事的流程,進行問答和討論,並以圖形的方式快速複述出來。
使用在線工具domain-story-modeler(https://github.com/WPS/domain-story-modeler),可以在線實踐domain storytelling。
可以更進一步,將通用語言中的詞彙提煉出來,將其中英文都列在通用語言詞彙表中,這些詞彙將會貫穿整個建模和設計過程,最終也會體現在代碼中,因此團隊中所有成員,都需要明確理解其含義,並且在相關討論、模型、以及代碼中使用它們。
通用語言
- 一種描述模型且基於模型的語言
- 團隊在進行所有交流時都使用它
- 代碼中也要體現
通用語言
- 類和操作的名稱
- 施加於模型之上的規則和約束
- 應用於領域模型的模式
2.4 分解問題:領域劃分和子域
2.4.1 什麼是領域劃分和子領域
領域劃分是以分離關注點爲原則對問題空間的劃分。基於領域劃分進行分工協作而非基於需求。
子域是領域中某個方面的問題和解決它所涉及的一切。
2.4.2 爲什麼要進行領域劃分
傳統模式的問題(6個需求分配給6個員工開發)
- 問題點和領域知識重疊
- 模型重疊
領域劃分可以解決傳統模式的問題
- 不同子領域聚焦解決不同問題
2.4.3 基於用戶故事分解的領域劃分方法
最終完整的劃分
- 交易域
負責面向終端用戶的商品交易,用戶購買商品時使用的設備前端界面、完成後臺交易的程序都在這裏。 - 支付域
- 商品域
- 用戶域
- 運營域
- 設備域
實現設備機售賣的管理和操作。
2.5 確定系統最核心的部分:核心域和精煉
戰略設計要明確核心域,團隊儘量減少非核心域投入。
2.5.1 子域的類型
子域
- 核心域(公司利益所在,相對穩定)
- 通用域
- 支撐子域
2.5.2 核心域的意義
公司利益所在,相對穩定。
2.5.3 什麼是精煉以及精煉的方法
精煉
- 分離出領域中普通的部分,從而得到領域中最精華的部分
精煉的方法
- 萃取
逐漸體現出關鍵作用的核心部分 - 分離
過濾掉不重要的部分
SmartRM核心域
- 交易域
- 運營域
2.6 分解模型:限界上下文
限界上下文是在解決方案空間對模型的分解單位。
2.6.1 什麼是限界上下文
限界上下文是一種語義上的上下文邊界。意思是在這個邊界裏的軟件模型組件都有它特定的含義並且做特定的事。一個限界上下文內的組件都是上下文特定的並且語義明確的。
2.6.2 爲什麼需要限界上下文
原因:
- 自然語言具有模糊性
- 同一個事物面向不同場景有不同模型
- 軟件系統需要分解模型以控制複雜性
- 限界上下文是分工的單位
理想情況,子域和限界上下文是一一對應的。
2.6.3 如何劃分限界上下文
三種方法
- Domain Storytelling 領域故事陳述法(建立通用語言、領域劃分,也可以深入解決方案空間建立模型)
- Event Storming 事件風暴法
- 基於子域概念提取
Domain Storytelling中邊界的特徵
- 單向聯繫
- 語義區別
- 活動的觸發方式不一樣
基於子域概念提取
2.6.4 限界上下文和微服務的關係
微服務是限界上下文的實現方式。
2.7 多個上下文之間如何協作?上下文映射和防腐層
2.7.1 什麼是限界上下文?爲什麼需要?
Context Mapping。
上下文映射是指限界上下文之間的模型映射關係。
描述團隊之間的協作關係以及上下文之間的集成關係。
決定上下文之間如何集成一級如何設置防腐層。
2.7.2 上下文映射有哪些模式?
九種模式
- Open Host Service (中臺、第三方公共服務:微信支付上下文)
服務提供方爲所有消費者提供一套公共的API。
針對通用的功能和模型。 - Conformist
沒有模型到模型的轉換。
一個上下文沿用另一個上下文的部分模型。 - Big Ball Of Mud(反模式,應該避免)
由混雜的模型構成的糟糕系統,模型不穩定且難以維護。
與大泥球合作的上下文要確保自身不被污染,設置防腐層。 - Anticorruption Layer
把上游上下文的模型轉換成自己上下文的模型。
是上游上下文中訪問外部模型的一個代理層。 - Shared Kernel
兩個上下文共享部分模型。
包括但不限於代碼、jar包、.so、數據庫表等。
慎用,僅當團隊緊密合作且共享部分穩定。 - Partenership
技術無關,是一種團隊協作關係。
兩個團隊之間可以隨時互通有無,協同變更。 - Customer/Supplier
下游上下文可以向上游上下文提需求。
一般用於核心域與非核心域之間的協作。 - Separate Ways
兩個上下文無協作,各自獨立。
當兩個上下文之間的集成成本過高。 - Published Language
標準化與協議化的模型。
所有上下文都可以與公開語言中的模型進行轉換。
對接了公開語言的上下文之間可以實現組件化對接。
2.8 剝離領域模型與技術實現:建立分層架構
2.8.1 爲什麼要隔離領域模型
原則:嚴格按照領域模型來編寫代碼
但是建模和實現中都有破壞該原則的因素。比如爲了性能,創建不存在的實體。
架構分層能夠避免模型在實現過程中被省略或者污染。
貧血模型:
- 圍繞數據(數據庫表)進行操作
傳統方式的問題
- 領域模型容易被省略,變成貧血模型
- 容易演變成基於數據的設計,一切從表結構開始
- 領域模型與技術實現混雜,易被技術實現綁架
2.8.2 DDD傳統四層架構和洋蔥(六邊形)架構
四層:
- 接口層:協議解析、封裝、消息路由
- 應用層:面向問題空間
- 領域層
- 基礎設施層:通用的框架和工具
四層優點:
- 分離關注點
- 讓領域模型層更獨立
- 單向依賴
四層缺點:
- 領域層對基礎設施層仍然有感知,領域模型和技術實現有耦合
解決方法
- 依賴倒置,將操作下放到基礎設置層,然後領域層裝配基礎設施層即可
六邊形架構優勢
- 保持領域層的純粹性,不受其他因素干擾
- 便於踐行模型驅動設計,代碼跟隨模型
- 便於把團隊精力集中到領域模型