領域模型(domain model)是對領域內的概念類或現實世界中物件的視覺化表示。領域模型也稱為概念模型、領域物件模型和分析物件模型。
——《UML和模式應用》
我們在日常開發中,經常針對一些功能點爭論“這個功能不應該我改,應該是你那邊改”,最終被妥協改了之後都改不明白為什麼這個功能要在自己這邊改。區別於傳統的架構設計,領域驅動設計(DDD)也許在這個時候能幫助你做到清晰的劃分。
什麼是DDD
領域驅動設計最初由Eric Evans提出,但是多年以來一直停留在理念階段,真正能實現並且落地的專案和公司少之又少,而進來阿里內部其實在大力推行DDD的理念,它主要可以幫助我們解決傳統單體式集中架構難以快速響應業務需求落地的問題,並且針對中臺和微服務盛行的場景做出指導。
DDD為我們提供的是架構設計的方法論,既面向技術也面向業務,從業務的角度來把握設計方案。
DDD的作用
統一思想:統一專案各方業務、產品、開發對問題的認知,而不是開發和產品統一,業務又和產品統一從而產生分歧。
明確分工:域模型需要明確定義來解決方方面面的問題,而針對這些問題則形成了團隊分鐘的理解。
反映變化:需求是不斷變化的,因此我們的模型也是在不斷的變化的。領域模型則可以真實的反映這些變化。
邊界分離:領域模型與資料模型分離,用領域模型來界定哪些需求在什麼地方實現,保持結構清晰。
DDD的概念
實體
有唯一標誌的核心領域物件,且這個標誌在整個軟體生命週期中都不會發生變化。這個概念和我們平時軟體模型中和資料庫打交道的Model例項比較接近,唯一不同的是DDD中這些實體會包含與該實體相關的業務邏輯,它是操作行為的載體。
值物件
依附於實體存在,通過物件屬性來識別的物件,它將一些相關的實體屬性打包在一起處理,形成一個新的物件。
舉個例子:比如使用者實體,包含使用者名稱、密碼、年齡、地址,地址又包含省市區等屬性,而將省市區這些屬性打包成一個屬性集合就是值物件。
聚合
實體和值物件表現的是個體的能力,而我們的業務邏輯往往很複雜,依賴個體是無法完成的,這時候就需要多個實體和值物件一起協同工作,而這個協同的組織就是聚合。聚合是資料修改和持久化的基本單元,同一個聚合內要保證事務的一致性,所以在設計的時候要保證聚合的設計拆分到最小化以保證效率和效能。
聚合根
也叫做根實體,一個特殊的實體,它是聚合的管理者,代表聚合的入口,抓住聚合根可以抓住整個聚合。
領域服務
有些領域的操作是一些動詞,並不能簡單的把他們歸類到某個實體或者值物件中。這樣的行為從領域中識別出來之後應該將它宣告成一個服務,它的作用僅僅是為領域提供相應的功能。
領域事件
在特定的領域由使用者動作觸發,表示發生在過去的事件。比如充值成功、充值失敗的事件。
四種模式
失血模型
模型中只有簡單的get set方法,是對一個實體最簡單的封裝,其他所有的業務行為由服務類來完成。
@Data @ToString public class User { private Long id; private String username; private String password; private Integer status; private Date createdAt; private Date updatedAt; private Integer isDeleted; }
public class UserService{ public boolean isActive(User user){ return user.getStatus().equals(StatusEnum.ACTIVE.getCode()); } }
貧血模型
在失血模型基礎之上聚合了業務領域行為,領域物件的狀態變化停留在記憶體層面,不關心資料持久化。
@Data @ToString public class User { private Long id; private String username; private String password; private Integer status; private Date createdAt; private Date updatedAt; private Integer isDeleted; public boolean isActive(User user){ return user.getStatus().equals(StatusEnum.ACTIVE.getCode()); } public void setUsername(String username){ return username.trim(); } }
充血模型
在貧血模型基礎上,負責資料的持久化。
@Data @ToString public class User { private Long id; private String username; private String password; private Integer status; private Date createdAt; private Date updatedAt; private Integer isDeleted; private UserRepository userRepository; public boolean isActive(User user){ return user.getStatus().equals(StatusEnum.ACTIVE.getCode()); } public void setUsername(String username){ this.username = username.trim(); userRepository.update(user); } }
脹血模型
service都不需要,所有的業務邏輯、資料儲存都放到一個類中。
對於DDD來說,失血和脹血都是不合適的,失血太輕量沒有聚合,脹血那是初學者才這樣寫程式碼。那麼充血模型和貧血模型該怎麼選擇?充血模型依賴repository介面,與資料儲存緊密相關,有破壞程式穩定性的風險。
建模方法
用例分析法
用例分析法是領域建模最簡單可行的方式。大致可以分為獲取用例、收集實體、新增關聯、新增屬性、模型精化幾個步驟。
-
獲取用例:提取領域規則描述
-
收集實體:定位實體,
-
新增關聯:兩個實體間用動詞關聯起來
-
新增屬性:獲取實體屬性
-
模型精化:可選的步驟,可以用UML的泛華和組合來表達模型間的關係,同時可以做子領域的劃分
四色建模法
四色建模法源於《Java Modeling In Color With UML》,它是一種模型的分析和設計方法,通過把所有模型分為四種型別,幫助模型做到清晰、可追溯。
簡單來說,四色關注的是某個人的角色在某個地點的角色用某個東西的角色做了某件事情。
事件風暴法
事件風暴法類似頭腦風暴,簡單來說就是誰在何時基於什麼做了什麼,產生了什麼,影響了什麼事情。
架構分層
區別於左圖傳統架構的分層,一般DDD分層會有一些變化。
Application:包含事件註冊、業務邏輯等
Domain:聚合、實體、值物件
InfraStructure:基礎設施封裝、資料庫訪問等
總結
DDD是一套完善的方法論,他能幫助我們合理的對系統進行架構設計,同時,好的模板應該是在不斷的適應變化,而DDD也能幫助我們更快速更方便的支撐業務的發展。