Domain Logic Approaches

爲每一個業務微服務或綁定上下文定義一個豐富域模型。html

你的目標是爲每一個業務微服務或綁定上下文 (BC) 建立一個內聚域模型。 但請記住,BC 或業務微服務有時可能由共享一個域模型的多個物理服務組成。 域模型必須捕獲它所表明的單個綁定上下文或業務微服務的規則、行爲、業務語言和約束。數據庫

域實體模式

實體表示域對象,主要由其標識、連續性和隨時間推移的持久性來定義,而不只僅由構成它們的屬性來定義。 正如 Eric Evans 所說,「主要由其標識定義的對象稱爲實體」。 實體在域模型中很是重要,由於它們是模型的基礎。 所以,應對其進行仔細識別和設計。dom

實體的標識能夠跨多個微服務或綁定上下文。微服務

同一標識(即,同一 Id,不過可能不是同一域實體)能夠跨多個綁定上下文或微服務建模。不過,這並不意味着具備相同屬性和邏輯的相同實體會在多個綁定上下文中實現。 相反,每一個綁定上下文中的實體會將其屬性和行爲限制爲該綁定上下文域中所需的屬性和行爲。測試

例如,買家實體可能具備某我的的大部分屬性,這些屬性在配置文件或標識微服務的用戶實體中定義,其中包括標識。 可是訂購微服務中的買家實體可能具備較少的屬性,由於只有某些買家數據與訂單流程相關。 每一個微服務的上下文或每一個綁定上下文都會影響其域模型。設計

除了實現數據屬性外,域實體還必須實現行爲。code

DDD 中的域實體必須實現與實體數據(在內存中訪問的對象)相關的域邏輯或行爲。 例如,做爲訂單實體類的一部分,你必須將業務邏輯和操做做爲任務(例如添加訂單項、數據htm

圖 7-8。 實現數據加行爲的域實體設計示例對象

固然,實體有時可能不會在實體類中實現任何邏輯。 若是某個聚合內的子實體沒有任何特殊邏輯,由於大多數邏輯都在聚合根中定義,則該子實體可能出現這種狀況。 若是你有一個複雜的微服務,它在服務類而非域實體中實現了大量邏輯,那麼你可能會陷入貧乏域模型中,下一節將對此進行解釋。進程

豐富域模型與貧乏域模型

Martin Fowler 在他的博客文章 AnemicDomainModel 中是這樣描述貧乏域模型的:

貧乏域模型的基本症狀是,乍一看上去像是真實存在的。 它包含一些對象,許多以域空間中的名詞命名,這些對象與真實域模型具備的豐富關係和結構相關聯。 但當你觀察它的行爲時,問題來了,你發現這些對象幾乎沒有任何行爲,徹底就是一些 getter 和 setter 而已。

固然,使用貧乏域模型時,將從一組可捕獲全部域或業務邏輯的服務對象(傳統上稱爲業務層)中使用這些數據模型。 業務層位於數據模型之上,就像使用數據同樣使用數據模型。

貧乏域模型就是一種程序化樣式設計。 貧乏實體對象不是真實的對象,由於它們缺少行爲(方法)。 它們只保存數據屬性,所以它不是一種面向對象的設計。 經過將全部行爲放到服務對象(業務層)中,實質上最終會產生面條式代碼或事務腳本,於是失去域模型提供的優點。

無論怎樣,若是微服務或綁定上下文很是簡單(CRUD 服務),僅包含數據屬性的實體對象形式的貧乏域模型可能已經足夠,不必實現更復雜的 DDD 模式。 在這種狀況下,它就是一個持久性模型,由於你特地建立了一個僅包含用於 CRUD 的數據的實體。

這就是爲何微服務體系結構特別適用於多體系結構方法(具體取決於每一個綁定上下文)。 例如,在 eShopOnContainers 中,訂購微服務實現了 DDD 模式,但目錄微服務(一種簡單的 CRUD 服務)沒有。

有人說貧乏域模型是一種反模式。 這真的取決於你要實現什麼。 若是你建立的微服務足夠簡單(例如,CRUD 服務),則採用貧乏域模型,它不是反模式。 可是,若是須要解決包含大量不斷變化的業務規則的微服務域的複雜性,那麼貧乏域模型多是該微服務或綁定上下文的反模式。 在這種狀況下,將其設計爲具備包含數據加行爲的實體的豐富模型並實現附加 DDD 模式(聚合、值對象等)可能對這種微服務的長期成功具備極大的好處。

其餘資源

  • DevIQ.Domain Entity \(域實體) https://deviq.com/entity/

  • Martin Fowler。The Domain Model \(域模型)https://martinfowler.com/eaaCatalog/domainModel.html

  • Martin Fowler。The Anemic Domain Model \(貧乏域模型)https://martinfowler.com/bliki/AnemicDomainModel.html

值對象模式

正如 Eric Evans 所指出的,「許多對象沒有概念標識。 這些對象用於描述某個事物的某些特徵。」

實體須要標識,但系統中的許多對象不須要,例如值對象模式。 值對象是一種沒有概念標識的對象,用於描述域方面。 這些對象實例化後可表示設計元素,你只會暫時關注它們。 你只關心它們是什麼,而不關心它們是誰。 其示例包括數字和字符串,但也能夠是更高級別的概念,例如屬性組。

一個微服務中的實體在另外一個微服務中可能就不是實體,由於在第二種狀況下,綁定上下文可能具備不一樣的含義。 例如,電子商務應用程序中的地址可能根本沒有標識,由於它可能僅表示我的或公司的客戶資料的一組屬性。 在這種狀況下,地址應歸類爲值對象。 可是,在電力公司的應用程序中,客戶地址對於業務領域可能很重要。 所以,該地址必須具備標識,以便計費系統直接連接到該地址。 在這種狀況下,地址應歸類爲域實體。

有名字和姓氏的人一般是一個實體,由於這我的具備標識,即便其名字和姓氏與另外一組值重合(例如這些名字還指另外一我的)也是如此。

值對象在關係數據庫和 ORM(如 EF)中很難管理,而在面向文檔的數據庫中,它們更易於實現和使用。

EF Core 2.0 包含實體功能,這樣能夠更易於處理值對象,如咱們稍後詳細介紹的同樣。

其餘資源

  • Martin Fowler。Value Object pattern \(值對象模式)https://martinfowler.com/bliki/ValueObject.html

  • Value Object \(值對象) https://deviq.com/value-object/

  • Value Objects in Test-Driven Development \(測試驅動開發中的值對象)https://leanpub.com/tdd-ebook/read#leanpub-auto-value-objects

  • Eric Evans。Domain-Driven Design:Tackling Complexity in the Heart of Software.(域驅動設計:軟件核心複雜性應對之道) (書;包括值對象的討論)\https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/

聚合模式

域模型包含不一樣數據實體和進程的羣集,能夠控制功能的重要方面,例如訂單履行或庫存。 聚合是一種粒度更細的 DDD 單元,用於描述一羣或一組可視爲內聚單元的實體和行爲。

一般基於所需事務來定義聚合。 一個典型的例子就是訂單,訂單中還包含訂單項列表。 訂單項一般是一個實體。 但它是訂單聚合內的子實體,該聚合還包含訂單實體做爲其根實體,一般稱爲聚合根。

識別聚合可能很難。 聚合是一組必須一致的對象,但不能按此挑選一組對象就將它們標記爲聚合。 你必須從域概念開始,並考慮在與該概念相關的最多見事務中使用的實體。 那些須要在事務上一致的實體就造成一個聚合。 考慮事務操做多是識別聚合的最佳方式。

聚合根或根實體模式

聚合由至少一個實體組成:聚合根,也稱爲根實體或主實體。 此外,它還能夠有多個子實體和值對象,全部實體和對象一塊兒共同實現所需的行爲和事務。

聚合根的目的是確保聚合的一致性;它應該是經過聚合根類中的方法或操做更新聚合的惟一入口點。 只能經過聚合根對聚合內的實體進行更改。 它是聚合的一致性守護者,它會考慮到可能須要在聚合中遵照的全部不變量和一致性規則。 若是單獨更改某個子實體或值對象,聚合根沒法確保聚合處於有效狀態。 這就像一張桌腳鬆動了的桌子。 保持一致性是聚合根的主要目的。

在圖 7-9 中,能夠看到一些示例聚合,例如買家聚合,其中包含一個實體(聚合根 Buyer)。訂單聚合包含多個實體和一個值對象。

相關文章
相關標籤/搜索