DDD 的一些基本概念
一、Entiry(實體)和 Value Object(值對象)
1、實體
實體的定義在原書《領域驅動設計》中的描述如下:
一些對象主要不是由它們的屬性定義的。它們實際上表示了一條 “標識線”,這條線跨越時間,而且常常經歷多種不同的表示。
領域驅動設計
聽起來比較晦澀,可以概括幾點:
1)、在同一類模型實中需要區別開來,一個實體是唯一的東西;
2)、每個實體有唯一標識來區別彼此;
3)、實體有生命週期,我們可以對它多次修改,但它仍然還是同一個實體。
2、值對象
當我們只關心一個模型元素的屬性時,或者說對於同一類模型實例我們不用區別每一個時,只關心其屬性時,這些對象就可以歸爲值對象。
相對於實體,值對象有幾個特徵:
1)不可變
即值對象是不能修改的,如果要修改則應該產生一個新對象,這樣就不用管理其整個生命週期。
同樣對於一個修改方法,以下是實體的僞代碼:
Public Class Order{
pulic void setStatus(Status status){
this.status = status;
}
}
如果是值對象,則僞代碼應該是這樣的:
Public Class Order{
pulic Order setStatus(Status status){
Order order = new Order(status);
return order;
}
}
2)、它沒有標識,兩個值對象是否相等只要比較屬性即可。
說了這麼多,我們可以舉一些例子:
訂單
這個模型在電商系統中比較常見,假如我昨天創建了一個訂單,今天也創建了一個訂單,在系統中這兩個訂單是不同的,兩個訂單通過訂單號來區別彼此,並且訂單需要管理其整個生命週期,包括從創建到支付再到發貨,因此訂單是實體。
發貨地址
如果我要將一個訂單發到深圳 XX 村 XX 街道,則我不用關心這個地址何時創建,我只需這個地址就行了,因此這個地址可以在昨天的訂單使用,也可以在今天的訂單使用,也沒必要創建相同的 2 個地址。因爲判斷 2 個地址是否相同,是通過對比省、市、村、街道等屬性,而不像上面的訂單中,通過訂單號來區別,因此地址是值對象。
上面這些舉例是基於電商的場景來說的,如果一些場景發生變化,實際的模型可能有變化,比如說對於快遞公司來說同一個目的地地址可能是一個宿舍,則這個地址需要表示爲實體了,因爲同一個地址可能對應多個目的地。
二、聚合
聚合是一組相關對象的集合,稱之爲修改單元。
每個聚合都有一個根和一個邊界,根一般是一個實體,外部對象只能引用根,內部對象之間則可以相互引用。
爲什麼需要聚合呢,原書給的原因如下:
1)、保證對象更改後的一致性;
2)、保持固定規則;
這裏還是以上面的訂單爲例,在電商系統中,一個完整的訂單除了訂單模型,還有地址、支付、物流等模型。
假如我們要修改發貨地址,如果我們不通過訂單去修改發貨地址,則一些規則無法保證,如防止訂單已經打包發貨了的情況下是不允許修改發貨地址的,如果先不從訂單得到地址,而是從數據庫中取出來直接修改地址,則這個規則可能被破壞了。
另外就有就是一個訂單還在,但是地址已經刪除了也是災難,因此所有對地址的修改都應該從訂單出去,先找到訂單,然後通過訂單去修改地址。
三、領域、子域、核心子域、支撐子域
領域是一個組織所做的事情以及其中包含的一切。
子域是領域一其中一個部分或者說某一個方面。
領域的概念太寬泛了,可以表示整個業務系統,而子域則是表示其中的一部分,之所以要這麼分,因爲分解是我們面對複雜系統的一個常用辦法,只要將系統拆分的足夠我們可以理解的範圍才容易掌握,這裏不用太糾結概念。
像公司做電商的,則電商就是你的領域,裏面可以分解爲商品子域、訂單子域、物流子域等。
核心子域
領域中最核心的子域,即是有價值,最核心的業務子域。
支撐子域
領域中比較通用的子域,起支撐的子域。
如電商系統中訂單應該是最核心的子域,短信、郵件發送可以作爲通知子域,後者主要起支撐使用,也是比較通用的,在其它系統中也是可以用的。
之所以要區分兩者 ,原書作者認爲應該將主要精力放在覈心子域,將技術最好的人投入到這些領域上,而支撐子域則用能力一般的人就可以,分清主、次。
實際上大部分公司是反過來的,認爲通用子域對人的要求更高,這會導致最核心的業務域沒有設計好。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/XsQ_9WoOdAPxhn-nXQyrNg