1. 程式人生 > 程式設計 >領域驅動設計DDD之實體

領域驅動設計DDD之實體

什麼是實體?

實體最主要有兩點特徵,一是唯一標識,二是連續性。

  • 唯一標誌: 當一些物件不是由屬性定義,而是由一個唯一標誌定義的話,我們就可以認為它是一個實體。好比我們不能通過一個人的外在特徵去唯一定位一個人,因為人從小到大,從年輕到衰老其外在特徵都是在改變的。而身份證號碼可以貫穿一個人的一生而不發生變化。而且唯一標識不一定僅有一個屬性表示,有可能通過多個屬性標識某一個唯一物件,就好比資料庫中的聯合外來鍵(可能有根據電話以及姓名唯一確定一個實體的情況)。

  • 連續性: 物件的連續性體現在物件是有生命週期的。在這個生命週期內,物件內的屬性可能是變化著的。好比銀行賬戶表,它就屬於一個實體,使用者的銀行卡號可以唯一的確定一個人的賬戶,而賬戶的內的餘額隨著時間變化(利息)或者隨著交易變化。

但實體並非一定是對映到我們現實世界的某個具體事物。實體可以是一個人,一輛車,也可以是一次交易或者是一場比賽。只要它們是滿足兩個條件的:一:它在整個生命週期是連續的,即變化的。可以通俗的理解為資料庫中的一個使用者資訊表,使用者的資訊會發生一些改變。而系統中的操作日誌表,我們不會再對其進行修改,我們也沒有必要對某個操作日誌做唯一的標識。二:它的區別不是由除了唯一標識之外的屬性所區分開的。比如資料庫中有一張銀行賬戶表,有卡號、餘額、賬戶類別三個欄位。我們區分每個賬戶並不是通過餘額和賬戶類別來區分一個銀行賬戶,而是通過唯一的銀行卡號來唯一區分。

實體不單單出現在同一張表或者同一個庫,甚至實體會出現在不同的系統當中。而當我們需要區分物件的時候,實體模型應該要定義出當符合什麼條件才能是算相同的事務。好比支付寶系統裡的使用者和銀行系統裡的使用者想要打通,並對使用者進行信用評分,那麼他們如何知道支付寶裡的某些流水和銀行裡的某些流水同屬於某一個人呢?這個時候可能就需要通過銀行開戶的身份證和支付寶當時實名認證的身份證來進行甄別。

實體建模

public class Customer {

    private Long customerID;

    private String name;

    private String phone;
    // 餘額
    private String balance;
    // 會員等級
    private String vipLevel;

}
複製程式碼

以上對Customer的建模我們可以適當地做一下改變,想想我們對實體最根本的理解,實體最主要的就是為了標識某個領域內的事物。在以上這個例子當中,餘額和會員等級其實都不能去標識一個使用者,而name欄位雖然不唯一,但我們也經常用來識別賬戶。而電話則是唯一的,因為註冊電話是唯一的。所以其實我們不僅可以通過CustomerID來識別某個使用者,還可以通過name和phone來查詢使用者。所以我們的準則是有助於識別某個領域物件的屬性我們放到實體當中去。

所以在上面的例子當中我們可以將餘額balance和vipLevel兩個欄位從Customer實體類中移出轉移到其他關聯物件上,並通過這些關聯物件履行職責。

public class Customer {

    private Long customerID;

    private String name;

    private String phone;

}
複製程式碼

生成實體唯一標識的四種方式

  1. 使用者提供一個或者多個初始唯一值作為輸入時,例如註冊賬戶時填入自定義使用者名稱或者郵箱或者電話等等。我們的系統必須保證這個值的唯一。
  2. 程式內部通過某種演演算法自動生成身份標識,例如UUID、雪花ID等機制去生成唯一演演算法。
  3. 程式依賴於持久化儲存,比如資料庫生成的自增主鍵
  4. 通過其他的限界上下文決定出的唯一標識,作為程式的輸入。

在某種特殊情況下,我們的實體是利用兩個屬性來進行標識物件,類似於資料庫表中的聯合主鍵,但是我們的資料庫框架如Hibernate或者Mybatis實際修改表中的資料可能用到的是表中的id欄位如updateByPrimaryKey()此類方法,這個時候我們可以讓實體繼承一個擁有id屬性的抽象類,以解決上述問題。

public abstract class IdentifiedDomainObject implements Serializable {

    private long id;

    protected long getId() {
        return id;
    }

    protected void setId(long id) {
        this.id = id;
    }

}
複製程式碼

實體的不變性。

有時候一個實體維護了一個或者多個不變條件,可以通俗的理解為我們的業務邏輯規則,比如一條訂單的支付金額不可能大於商品總額,也不可能為負數。實體必須在整個生命週期中都保持這種一致性否者就是錯誤的。不變條件主要是由聚合所關注(後文會談到)。我們可能還需要去驗證實體內的屬性是否符合我們的要求,如不能為null,不能為空串,長度不能大於100,需滿足一定格式等等。我們可以在為實體填入引數的時候進行判別。

關於DDD的理解各有不同,歡迎網友評論一起探討。

轉自我的個人部落格 vc2x.com