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