DDD學習(二)—— 領域建模重要概念

Jerry1ee發表於2020-11-22

領域建模重要概念

在閱讀之前應該理解了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個特徵:

  1. 與領域概念相關的操作不是ENTITY或VALUE OBJECT的一個自然組成部分。
  2. 介面是根據領域模型的其他元素定義的。
  3. 操作是無狀態的。

領域服務是用來協調領域物件完成某個操作,而不屬於某個物件;但與物件關係緊密,需要參照物件來設計;用來處理業務邏輯,它本身是一個行為,所以是無狀態的。狀態由領域物件(具有狀態和行為)儲存。

上面也說了,領域物件是具有狀態和行為的。那就是說我們也可以在實體或值物件來處理業務邏輯。那我們該如何取捨呢?
一般來說,在下面的幾種情況下,我們可以使用領域服務:

  • 執行一個顯著的業務操作過程
  • 對領域物件進行轉換
  • 以多個領域物件為輸入,返回一個值物件。

比如在銀行系統中,轉賬應該是一個領域服務,它包含了很多步驟,涉及轉賬方餘額校驗,收款方是否符合規定,扣款,餘額增加,保持事務,轉賬失敗回滾等等業務領域的操作,這些操作無法直接被某個領域物件完全收斂,所以選擇以領域服務進行實現。

與應用服務不同

注意:要將領域服務和應用服務進行區分!

應用服務是用來表達用例和使用者故事(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/

相關文章