談一下你對DDD的理解?我:馬什么梅?
領域模型(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也能幫助我們更快速更方便的支撐業務的發展。
本文轉載自微信公眾號「科技繆繆」,可以通過以下二維碼關注。轉載本文請聯系科技繆繆公眾號。