C 高階系列 DDD領域驅動設計初探(一) 聚合

2021-10-01 23:47:51 字數 3951 閱讀 7995

ddd領域驅動設計初探系列文章:

根據《領域驅動設計:軟體核心複雜性應對之道.eric.eva》書中的觀點,領域模型是軟體專案的公共語言的核心,是領域專家和開發人員共同遵守的通用語言規則,那麼在ddd裡面,建模的重要性不用多說,所以要想更好理解領域驅動設計,理解領域模型的劃分和建立就變得相當必要。首先來看看ddd裡面幾個比較重要的概念:

1、領域模型:領域模型與資料模型不同,它表述的是領域中各個類及其之間的關係。從領域驅動設計的角度看,資料庫只不過是儲存實體的乙個外部機制,是屬於技術層面的東西。資料模型主要用於描述領域模型物件的持久化方式,應該是先有領域模型,才有資料模型,領域模型需要通過某種對映而產生相應的資料模型,從這點來說,最新的ef的code first就是乙個很好的體現。領域模型物件分為實體、值物件和服務。

2、實體:在領域驅動設計裡面,實體是模型中需要區分個體的物件。這裡的實體和entityframework裡面的實體不是乙個概念,ef的實體為資料實體,不包含物件的行為。就博主的理解,ddd概念裡面的實體就是包括實體資料(ef的model)和行為的結合體

3、值物件:通過物件屬性值來識別的物件,它將多個相關屬性組合為乙個概念整體。相比實體而言,值物件僅僅是比實體少了乙個標識。值物件的設計比較存在爭議,我們暫且記住值物件和實體的區別:(1)實體擁有唯一標識,而值物件沒有;(2)實體允許變化,而值物件不允許變化;(3)判斷兩個實體相等的方法是判斷實體的標識相等,而判斷兩個值物件相等的標準是值物件內部所有屬性值相等;

4、聚合(以及聚合根):聚合表示一組領域物件(包括實體和值物件),用來表述乙個完整的領域概念。而每個聚合都有乙個根實體,這個根實體又叫做聚合根。舉個簡單的例子,乙個電腦包含硬碟、cpu、記憶體條等,這乙個組合就是乙個聚合,而電腦就是這個組合的聚合根。博主覺得關於聚合的劃分學問還是挺大的,需要在實踐中慢慢積累。同乙個實體,在不同的聚合中,它可能是聚合根,也可能不是,需要根據實際的業務決定。聚合根是聚合所表述的領域概念的主體,外部物件需要訪問聚合內的實體時,只能通過聚合根進行訪問,而不能直接訪問

5、領域服務:博主的理解,領域模型主張富領域模式,也就是說把領域邏輯盡量寫在領域實體裡面,也就是常說的「充血模式」,而對於業務邏輯,最好是以服務的形式提供。至於領域邏輯和業務邏輯的界定,這個要根據實際情況來定。總之,領域服務是用來處理那些領域模型裡面不好定義或者某些可變邏輯的的時候才會用到。待驗證!

6、工廠、倉儲等概念留在demo裡面說明。

領域驅動設計將軟體系統分為四層:基礎結構層、領域層、應用層和表現層。來看看書中的分層:

其實在dax.net的系列中這張圖更能說明這種架構

博主打算用許可權系統的案例說明的領域驅動設計的專案架構。專案嚴格按照表現層、應用層、領域層、基礎設施層來劃分。

表現層:mvc的web專案,負責ui呈現。

應用層:wcf服務,負責協調領域層的呼叫,向ui層提供需要的介面。

領域層:定義領域實體和領域邏輯。

基礎設施層:一些通用的技術,比如aop、mef注入、通用的工具類、dto模型層,這裡為什麼要有乙個dto模型層,dto是用於ui展現用的純資料model,它不包含實體行為,是一種貧血的模型。

整個專案的呼叫方式嚴格按照ddd設計來進行,ui層通過wcf服務呼叫應用層的wcf介面,wcf服務通過倉儲呼叫領域層裡面的介面,基礎設施層貫穿其他各層,在需要的專案中都可以引用基礎設施層裡面的內庫。

接下來,博主就根據自己的理解,從零開始使用這種架構寫乙個簡單的許可權管理系統。由於是領域驅動設計,所以,文章的重點會放在領域層,專案使用了ef的model first來進行,先設計實體,後生成資料庫。

3.1 首先來看看表結構

根據博友要求,這裡說明一下表之間的對映關係:

1表示tb_department表的主鍵department_id作為tb_users表的外來鍵;

2表示tb_users表的主鍵user_id作為tb_userrole表的外來鍵;

3表示tb_role表的主鍵role_id作為tb_userrole表的外來鍵;

4表示tb_role表的主鍵role_id作為tb_menurole表的外來鍵

5表示tb_menu表的主鍵menu_id作為tb_menurole表的外來鍵

首先建好對應的表實體,然後根據模型生成資料庫

將生成的sql語句執行後就可以得到對應的表結構。

3.2 聚合的劃分

在領域層裡面我們新建乙個basemodel,裡面有三個類

這三個類ientity、iaggregateroot、aggregateroot分別定義了實體的介面、聚合根的介面、聚合根的抽象實現類。

//

用作泛型約束,表示繼承自該介面的為領域實體

public

inte***ce

ientity

///

///聚合根介面,用作泛型約束,約束領域實體為聚合根,表示實現了該介面的為聚合根例項,由於聚合根也是領域實體的一種,所以也要實現ientity介面

/// public

inte***ce

iaggregateroot:ientity

///

///聚合根的抽象實現類,定義聚合根的公共屬性和行為

/// public

abstract

class

aggregateroot:iaggregateroot

這裡定義介面的作用是定義實體和聚合根的泛型約束,抽象類用來定義聚合根的公共行為,目前為止,這些介面和類裡面都是空的,後面會根據專案的需求一步一步往裡面加入邏輯。

在ef裡面由edmx檔案會生成實體的屬性,前面說到領域模型主張充血模式,所以要在ef的實體model裡面加入實體的行為,為了不改變ef生成實體的**,我們使用partial類來定義實體的行為。我們來看model資料夾下面的**

public

partial

class

tb_department: aggregateroot

}

public

partial

class

tb_menu : aggregateroot

///

///由於不會直接操作此表,所以tb_menurole實體不必作為聚合根,只是作為領域實體即可

/// public

partial

class

tb_menurole:ientity

public

partial

class

tb_role:aggregateroot

public

partial

class

tb_userrole:ientity

public

partial

class

tb_users:aggregateroot

我們看到,這些實體,只有tb_menurole和tb_userrole不是聚合根,其他實體都是聚合根。我這裡大概劃分為4個聚合:

聚合1:tb_department實體

聚合2:tb_menu、tb_menurole、tb_role這3個為乙個聚合,聚合根是tb_menu。

聚合3:tb_users、tb_userrole、tb_department、tb_role這4個為乙個聚合,聚合根是tb_users。

聚合4:tb_role、tb_users、tb_userrole、tb_menurole、tb_menu這5個表為乙個聚合,聚合根是tb_role。

可能這樣分會有一定的問題,後續出現再慢慢糾正。

到這裡,聚合的劃分基本完了,至於為什麼要做這麼一些約束和設計,因為倉儲只能對聚合根做操作,下篇講倉儲的時候會詳細說明。

ddd博大精深,文中很多觀點為博主個人理解,可能不太成熟或者有誤,歡迎拍磚~~

DDD領域驅動設計

公司裡面敏捷專案要講ddd領域驅動設計,加緊學習了一下,找了一些資料研究。eric evans的 domain driven design領域驅動設計 簡稱ddd,evans ddd是一套綜合軟體系統分析和設計的物件導向建模方法,本站jdon.com是國內公開最早討論ddd 之一,可訂閱 ddd專題...

DDD(領域驅動設計)

domain 領域 driven 驅動 design 設計 由eric evans最先提出,目的是對軟體所涉及到的領域進行建模,以應對系統規模過大時引起的軟體複雜性的問題。整個過程大概是這樣 開發團隊和領域專家一起通過 通用語言 ubiquitous language 去理解和消化領域知識,從領域知...

DDD領域驅動設計

極客時間學習筆記 為什麼微服務設計的時候需要ddd?1 軟體架構模式演進的三個階段 第一階段是單機架構 第二階段是集中式架構 第三階段是分布式微服務架構 2 在單機和集中式架構這兩種模式下,軟體無法快速響應需求和業務的迅速變化,最終錯失發展良機。3 微服務拆分困境產生的根本原因就是不知道業務或者微服...