一位荷蘭程式設計師眼中的DDD - hexmaster

banq發表於2019-08-29

這裡有一些關於DDD的想法。我真的很喜歡DDD(領域驅動設計)的思想和原則,我真的建議你去研究它。這就是為什麼現在是新部落格的時候了。我們稱之為C#開發人員DDD的實用介紹。

這是系列的第一篇文章。這篇文章介紹了DDD以及如何構建領域模型。

那麼,什麼是DDD?您可能知道縮寫的含義,但它究竟意味著什麼?這個問題的答案很簡單但很複雜。DDD是一件很大的事情,涉及很多東西,但基本上你只是在不同的域中劃分系統的功能。在網上商店的經典示例中,目錄,購物籃和訂單流程都將位於單獨的域中。這也可能是DDD和微服務如此良好結合的原因,但是利用DDD的強大功能並不一定意味著您的技術架構必須是微服務。您還可以在巨大的單體巨石中享受DDD的優勢。

您在域中打包的所有功能稱為有界上下文。在啟動微服務架構時,您可能希望在單獨的微服務中使用每個有界上下文,儘管並非所有情況都適用,請務必評估您的決策。現在在這個DDD世界中,還有一個人稱為領域專家。這個人是指定域名中最聰明的人,可以告訴你關於它的一切。與敏捷/ Scrum相比,您可以將域專家識別為產品所有者,但是針對特定域。不同領域的專家實際上可能是一個人。這是您可能感到困惑的重要資訊。擁有不同領域的不同專家也可能會引入術語上的差異。

正如詞語所說,有界的上下文圍繞它們有一個巨大的界限。這意味著域中涉及的所有功能和基礎結構都與其他域分離。例如,不同的域不應共享相同的資料儲存。在處理某個實體應該存在於多個域(例如使用者和客戶)的情況時,這可能會變得有點挑戰,必須配置訊息傳遞系統以同步域之間的更改。

當然,如果資料儲存是分開的,這是推薦做法,最終的一致性非常重要。

一定要有一個好的解決方案。如果使用者在使用者服務中更改了他的電子郵件地址並下了訂單,則您不希望訂單服務向舊地址傳送確認電子郵件。新的電子郵件地址應與訂單服務同步,以便“知道”新地址。DDD的一個重要規則是隻有一個域可以更改某個實體。因此,如果電子郵件地址屬於使用者,則只有使用者服務可以更改它。所有域都可以使用使用者的電子郵件欄位,但只有一個可以更改它。

那對我來說有什麼用呢?

DDD方式有什麼優勢?一個人知道的公司所有流程並不多。像亞馬遜這樣龐大的線上網路商店中,只有C級經理知道商品包裝過程的細節,讓這個人成為包裝過程的領域專家是有道理的, 包裝軟體可能包含“包裝過程人員”所知的術語和名稱,域專家和軟體解決方案之間沒有翻譯。

集中知識是關鍵,因為企業能夠確保理解軟體不會被鎖定在“部落知識”中。這意味著有關軟體功能的資訊是開放的,每個人都可以做出貢獻。開發人員(不再)是唯一知道整個業務流程的人。

最後,我認為與傳統技術相比,使用DDD原理的精心設計的軟體更容易維護。而且我經歷了“牽一動百'經歷。使用DDD以後所有活動元件仍然存在,但不再相互依賴。

第一個領域模型

基礎很簡單,我想為User使用者建立一個域模型。使用者具有ID,名稱,電子郵件地址和密碼。然後我還想跟蹤建立和到期日期。所以我從幾個屬性開始:

public Guid Id { get; private set; }
public string DisplayName { get; private set; }
public string EmailAddress { get; private set; }
public Password Password { get; private set; }
public DateTimeOffset CreatedOn { get; private set; }
public DateTimeOffset ExpiresOn { get; private set; }

請注意,所有屬性的setter都是私有的。這是為了防止外部系統更改屬性的值。例如,您可能希望驗證EmailAddress屬性。如果EmailAddress屬性是公共的(因此可以為其他類設定),則無法保證屬性的值始終正確,因此您無法保證域模型的有效狀態。所以相反,所有的setter都是私有的,所以沒有人可以損害我們域模型的正確狀態。現在要更改電子郵件地址,我們需要新增一個這樣做的方法。

public void SetEmailAddress(string value)
{ 
    if(string.IsNullOrWhiteSpace(value))
    { 
        throw new ArgumentNullException(nameof(value)); 
    } 
    if(!Equals(EmailAddress,value))
    { 
        if(value.IsValidEmailAddress())
        { 
            EmailAddress = value; 
        } 
        else 
        { 
            throw new ArgumentException($“value {value}不是有效的電子郵件地址”); 
        } 
    } 
}

您可以看到電子郵件地址的所有驗證(或此係統所需的驗證)都是在SetEmailAddress()方法內完成的,而EmailAddress屬性的有效性僅在新電子郵件地址根據業務規則有效時才會更改。順便說一下,這些業務規則由域專家定義。

我認為域模型有兩種建構函式,一種建構函式是建立一個新物件,例如一個使用者。第二個是從(例如)資料儲存重新生成現有物件。不同之處在於(在我看來):建立新使用者時,您將最小的必填欄位傳遞給建構函式以建立新的有效域模型。

在此示例中,使用者的電子郵件地址是必需的。讓我們說它是唯一的必填欄位。然後,新使用者的建構函式將只接受一個引數,即電子郵件地址。建構函式將建立域模型類的新例項,呼叫SetEmailAddress()方法來設定傳遞的電子郵件地址並返回新建立的物件。這樣,電子郵件地址上的所有驗證都會得到驗證,以便在一切正常時:

public User(string emailAddress)
{ 
    Id = Guid.NewGuid(); 
    SetEmailAddress(EMAILADDRESS); 
    CreatedOn = DateTimeOffset.UtcNow ;; 
}

現在,如果您有關於使用者的更多資訊,讓我們說出他的顯示名稱和密碼,您可以建立其他Set方法,如SetEmailAddress()方法,驗證傳遞的資訊,然後在一切正常後立即更改屬性值。您還看到我也新增了一些預設值。

現在,您可以將該使用者傳遞到某個儲存庫,以便儲存安全的地方。現在,如果您想要更改某個使用者的資訊,可以從資料儲存中獲取該資訊並重現域模型。

此過程使用第二個建構函式。第二個建構函式接受域模型中的所有欄位,並立即建立它。

public User(Guid id, 
    string emailAddress, 
    string displayName,
    Password pwd, 
    DateTimeOffset created,
    DateTimeOffset expires)
{
    Id = id;
    EmailAddress = emailAddress;
    DisplayName = displayName;
    Password = pwd;
    CreatedOn = created;
    ExpiresOn = expires;
}

所以我希望你現在正在思考,也許已經可以看到DDD的好處了。

相關文章