DDD學習(二)—— 領域建模重要概念
領域建模重要概念
在閱讀之前應該理解了DDD的主要思想和 限界上下文,子域核心域等基礎概念
實體(Entity)
- 重要概念:標識,可能由唯一一條屬性,或多種屬性組合而成。
概念性解釋
一些物件主要不是由它們的屬性定義的。它們實際上表示了一條“標識線”(A Thread of Identity),這條線跨越時間,而且常常經歷多種不同的表示。有時,這樣的物件必須與另一個具 有不同屬性的物件相匹配。而有時一個物件必須與具有相同屬性的另一個物件區分開。錯誤的標識可能會破壞資料。
主要由標識定義的物件被稱作ENTITY。ENTITY(實體)有特殊的建模和設計思路。它們具 有生命週期,這期間它們的形式和內容可能發生根本改變,但必須保持一種內在的連續性。為了 有效地跟蹤這些物件,必須定義它們的標識。它們的類定義、職責、屬性和關聯必須由其標識來 決定,而不依賴於其所具有的屬性。即使對於那些不發生根本變化或者生命週期不太複雜的 ENTITY,也應該在語義上把它們作為ENTITY來對待,這樣可以得到更清晰的模型和更健壯的實現。
當然,軟體系統中的大多數‚ENTITY‛並不是人,也不是其通常意義上所指的‚實體‛或‚存 在‛。ENTITY可以是任何事物,只要滿足兩個條件即可:
- 一是它在整個生命週期中具有連續性
- 二是它的區別並不是由那些對使用者非常重要的屬性決定的。
比如小明想去看一場演唱會,買了一張門票,門票上寫著座位號為 4排3座 ,那麼該座位在建模時應當是實體,因為小明應該只能坐 4排3座 的座位,其他位置的座位不屬於他。此時 4排3座就是一個唯一性標識。但如果演唱會門票改為入場券形式,即門票上只說明可以憑此票擁有一個座位,那麼此時的座位就不再是實體,而應該是“值物件”,也就是說每個座位都是一樣的,只是用來坐而已。後一節我們將會詳細瞭解值物件的概念。
注意:將與標識有關的屬性留在Entity裡,將無關行為和屬性轉移到與核心實體關聯的其他物件中。
識別時注意:是否可以互換?
很多物件不是通過它們的屬性定義的,而是通過連續性和標識定義的。
建立策略
建立實體身份標識的策略,從簡單到複雜:
- 使用者提供一個或多個初始值作為程式輸入,應該保證這些初始值是唯一的
- 程式自身通過一些演算法、類庫、框架生成唯一標識
- 持久化儲存時,如資料庫儲存時,來生成唯一標識
- 另一個限界上下文決定出了唯一標識,可以作為程式輸入
總結重要屬性
- A1: 具有唯一標識(Identity)唯一標識可以是一條以上的屬性,有四種生成途徑,不同的生成時間
- A2: 在整個生命週期中連續
- A3: 擁有其他 屬性(Attribute) 和 實體行為(Operation)
- A4: 使用領域事件和事件儲存跟蹤連續性變化
值物件(Value Object)
- 很多物件沒有概念上的標識,它們描述了一個事務的某種特徵
- 不變的物件可以自由的共享,如需改變只能整體替換
概念性解釋
用於描述領域的某個方面而本身沒有概念標識的物件稱為VALUE OBJECT(值物件)。VALUE OBJECT被例項化之後用來表示一些設計元素,對於這些設計元素,我們只關心它們是什麼,而不 關心它們是誰。
VALUE OBJECT可以是其他物件的集合。在房屋設計軟體中,可以為每種窗戶樣式建立一個物件。我們可以將“窗戶樣式”連同它的高度、寬度以及修改和組合這些屬性的規則一起放到“窗戶”物件中。這些窗戶就是由其他VALUE OBJECT組成的複雜VALUE OBJECT。它們進而又被合併到更大的設計元素中,如“牆”物件。
VALUE OBJECT經常作為引數在物件之間傳遞訊息
VALUE OBJECT可以用作ENTITY(以及其他VALUE)的屬性
VALUE OBJECT應該是不可變的
對值物件的持久化儲存
如果一個實體持有值物件集合引用,該如何去儲存呢?例如,在許可權管理系統中,一個使用者A,持有值物件集合{建立者,編輯者,檢視者},每個值物件也是立體的物件,包括各種屬性,那麼如何將這種關係持久化儲存呢?
-
多個值物件序列化到單個列中
- 即把各個角色物件(建立者等)拼接起來,存到單獨的列中,但
- 列寬有所限制
- 如果拼接的屬性需要充當查詢條件,會比較麻煩
- 如果拼接的屬性比較複雜,要自定義型別來做
-
使用資料庫實體保持多個值物件
- 這裡實際上是以資料建模的角度把值物件當作實體儲存,但值物件在領域模型中依然還是值物件
- 舉個例子,使用者A的id為1,這個id是用來做唯一標識和跟蹤的,有了這個id1,使用者A就是一個實體
- 採用持久化委派主鍵的方法,讓使用者A下的值物件(建立者等)也擁有其id作為屬性,只在儲存到資料庫時才生效
-
使用聯合表儲存多個值物件
- 值物件儲存到另一個表中
- 再用外來鍵關聯到實體的唯一標識上
總結重要屬性
- A1: 不變性(一旦建立,不可修改,只能替換)
- A2: 不是領域中的一個東西,只是用於度量或描述領域中某個東西的概念
- A3: 有意義整體(500美元,500和美元組合起來才有意義)
- A4: 相等性(所有屬性值相等則兩個值物件相等)
領域服務(Service)
-
強調與其他物件的關係
-
往往以一個活動來命名,是動詞而不是名詞
概念性解釋
在某些情況下,最清楚、最實用的設計會包含一些特殊的操作,這些操作從概念上講不屬於任何物件。與其把它們強制地歸於哪一類,不如順其自然地在模型中引入一種新的元素,這就是SERVICE(服務)。
好的SERVICE有以下3個特徵:
- 與領域概念相關的操作不是ENTITY或VALUE OBJECT的一個自然組成部分。
- 介面是根據領域模型的其他元素定義的。
- 操作是無狀態的。
領域服務是用來協調領域物件完成某個操作,而不屬於某個物件;但與物件關係緊密,需要參照物件來設計;用來處理業務邏輯,它本身是一個行為,所以是無狀態的。狀態由領域物件(具有狀態和行為)儲存。
上面也說了,領域物件是具有狀態和行為的。那就是說我們也可以在實體或值物件來處理業務邏輯。那我們該如何取捨呢?
一般來說,在下面的幾種情況下,我們可以使用領域服務:
- 執行一個顯著的業務操作過程
- 對領域物件進行轉換
- 以多個領域物件為輸入,返回一個值物件。
比如在銀行系統中,轉賬應該是一個領域服務,它包含了很多步驟,涉及轉賬方餘額校驗,收款方是否符合規定,扣款,餘額增加,保持事務,轉賬失敗回滾等等業務領域的操作,這些操作無法直接被某個領域物件完全收斂,所以選擇以領域服務進行實現。
與應用服務不同
注意:要將領域服務和應用服務進行區分!
應用服務是用來表達用例和使用者故事(User Story)的主要手段。
應用層通過應用服務介面來暴露系統的全部功能。在應用服務的實現中,它負責編排和轉發,它將要實現的功能委託給一個或多個領域物件來實現,它本身只負責處理業務用例的執行順序以及結果的拼裝。通過這樣一種方式,它隱藏了領域層的複雜性及其內部實現機制。
除了定義應用服務之外,在該層我們可以進行安全認證,許可權校驗,持久化事務控制,或者向其他系統發生基於事件的訊息通知,另外還可以用於建立郵件以傳送給客戶等。
應用層作為展現層與領域層的橋樑。展現層使用VO(檢視模型)進行介面展示,與應用層通過DTO(資料傳輸物件)進行資料互動,從而達到展現層與DO(領域物件)解耦的目的。
比如轉賬的一系列操作,當轉賬完成後,傳送簡訊通知使用者,這個行為我們更偏向於將它建模為應用服務。很顯然,這個傳送簡訊與轉賬這一業務沒有關係,我們要確保領域服務只關心業務邏輯即純粹性。
不要過度使用領域服務
過度使用領域服務將會產生一個貧血模型,例如資料建模時,我們的實體常用只含有get/set方法,所有的業務邏輯都包含在了service。這樣導致service變成了一個大泥球。注意區分領域服務與實體,值物件行為。
總結重要屬性
- A1: 無狀態的
- A2: 輸入:多個領域物件;輸出:一個值物件
- A3:以一個活動命名,是動詞而非名詞
領域事件
概念性解釋
- 領域事件通常是用來與其他聚合解耦的,採用觀察者模式,一個聚合訂閱另外一個聚合的事件。
領域事件是一個領域模型中極其重要的部分,用來表示領域中發生的事件。忽略不相關的領域活動,同時明確領域專家要跟蹤或希望被通知的事情,或與其他模型物件中的狀態更改相關聯。
- 由於不同的聚合之間通訊主要採取聚合根進行,所以有必要採用領域事件作為媒介
- 可以採用訊息匯流排來進行實現,需要接收變化的聚合根註冊到事件匯流排上,一旦發生變化,通知所有註冊的聚合根
- 如果是同一個限界上下文時,通常採用程式內的訊息釋出領域事件;如果是在不同限界上下文時,我們可以採用訊息中介軟體,比如rocket mq,kafka進行訊息的訂閱與釋出。
領域事件更加關注最終一致性,而不是原子性。
可以對照 事件匯流排 來進行理解。
領域事件經常被用來充當不同聚合間交換資訊的媒介,我們知道聚合的內部是統一的,但有時也要保證不同聚合間的一致性,但又不能直接去做,因為讓不同聚合內部的屬性同步,違反了聚合的設計理念,所以用領域事件來達到最終一致性。
總結重要屬性
- A1: 由聚合上的命令產生,命名反映已經發生的事件
- A2: 具有唯一身份標識(Identity)
- A3: 包括髮起方 和 參與者
- A4: 可以通過呼叫領域服務建立
參考資料:
作者:『聖傑』
出處:http://www.cnblogs.com/sheng-jie/
相關文章
- DDD建模心得:領域概念建模是一種語文語法分析練習 - prefactordesign語法分析
- DDD之2領域概念
- DDD領域設計概念梳理
- DDD+Javascript領域建模示例 -Alex LawrenceJavaScript
- 使用Typescript實現DDD領域建模 - Matthew de NobregaTypeScript
- 用形而上學進行領域建模
- DDD-領域物件與領域服務物件
- DDD領域驅動設計:領域事件事件
- 領域驅動設計(DDD)中模型的重要性 - Jeronimo模型
- 領域驅動模型DDD(二)——領域事件的訂閱/釋出實踐模型事件
- 如何進行高質量的DDD領域建模?什麼是領域模型?如何捕捉?尺寸如何? - Manning模型
- 運用領域模型——DDD模型
- 基於COLA架構建立運輸微服務應用和DDD領域建模架構微服務
- 在DDD中建立領域模型模型
- DDD:不要洩露領域事件事件
- 一張圖解釋DDD領域驅動設計的戰術概念圖解
- 領域驅動設計的DDD與ddd - nick
- DDD領域驅動設計pdf
- DDD劃分領域、子域,核心域,支撐域的目的
- 讀書系列-《解構領域驅動》-領域概念
- DDD-領域驅動設計示例
- 淺談DDD(領域驅動設計)
- ABP與DDD領域驅動關係
- 淺談 DDD 領域驅動設計
- DDD領域驅動設計:倉儲
- 領域驅動設計(DDD)實踐之路(二):事件驅動與CQRS事件
- SAP智慧領域概念區分
- 領域驅動設計(DDD)入門&概要
- DDD-領域驅動設計簡談
- dayatang/dddlib:DDD領域驅動設計庫
- 領域本體與DDD的UL語言
- 領域驅動設計 (DDD) 簡介 - jannikwempe
- 架構師之路 - 業務領域建模架構
- 機器學習中比較重要的幾個概念機器學習
- 想入門資料科學領域?明確方向更重要資料科學
- 領域驅動設計核心概念
- 領域驅動設計(DDD)高手養成記
- 領域驅動設計(DDD)實踐之路(一)