基于事件驱动架构构建微服务第2部分:领域对象和业务规则
原文鏈接:https://logcorner.com/building-microservices-through-event-driven-architecture-part2-domain-objects-and-business-rules/
在本文中,我將實現(xiàn)領域模型:
EduSync.Speech.Domain
這是包含核心域的最內(nèi)層。它包含我們的領域?qū)ο蠛蜆I(yè)務規(guī)則。并定義我們的外部接口。
不允許使用數(shù)據(jù)庫、網(wǎng)絡連接、文件系統(tǒng)、UI或特殊框架。
核心領域?qū)ψ陨硪酝獾娜魏问挛镆粺o所知。
這些依賴項及其實現(xiàn)使用接口注入到我們的核心域中。
在上一步結(jié)束時,我們最終得到了一個貧血領域模型。所以讓我們從豐富它開始。
充血領域模型
貧血領域模型是DDD世界中的一種反模式,因此在本節(jié)中,我將使用值對象將領域模型與數(shù)據(jù)契約分離。
貧血領域模型是一種領域模型,其中數(shù)據(jù)和對該數(shù)據(jù)的操作彼此分離。換句話說,只有屬性的類和處理這些屬性的方法位于另一個類中。
因此,這些其他類既可以讀取數(shù)據(jù),也可以修改數(shù)據(jù)。所以領域類必須有public setter。這是缺乏封裝反模式。
讓我們從驗證Title開始。
我的第一個測試是:Title長度必須大于10個字符且小于60個字符:
測試將失敗,因此讓我們實現(xiàn)Title驗證:
Title值對象
實體和值對象的主要區(qū)別在于如何識別它們。
實體由引用相等和標識相等標識。
值對象由引用相等和結(jié)構(gòu)相等來標識。
引用相等:如果兩個對象引用內(nèi)存中的同一個對象,則它們相等
標識相等:如果兩個對象具有相同的標識,則它們相等
結(jié)構(gòu)相等:如果兩個對象的所有成員都相等,則兩個對象相等
實體具有Id字段并且是可變的,而值對象沒有Id字段并且是不可變的。
值對象沒有實體就沒有意義,它必須屬于一個實體。
考慮以下情況:
2輛相同型號、相同顏色、相同年齡等的車輛……總是2輛不同的車輛,因為每輛車都有自己的標識:車輛是一個實體。
2個所有字段都相等的地址(相同的街道號碼、相同的城市、相同的國家,等等)是完全相同的地址:地址是一個值對象。
Title的第一個實現(xiàn)如下所示:
請記住,值對象由引用相等和結(jié)構(gòu)相等來標識.
所以右鍵單擊Title類并選擇生成 Equals和GetHashCode。
Title只有一個值,因此選擇它并單擊確定
Title現(xiàn)在是一個值對象,它的最終實現(xiàn)看起來像這樣
這是Title值對象的單元測試。如果它們具有相同的值,我應該驗證2個標題是否相等,如果不是,則不同
URL值對象
驗證Url的所有邏輯都在名稱為UrlValue的值對象中實現(xiàn)
Type值對象
驗證SpeechType的所有邏輯都在名稱為SpeechType的值對象中實現(xiàn)
Speech領域?qū)ο笕缦滤?#xff1a;
實體和聚合
請記住,實體由引用相等和標識相等標識并具有Id字段。因此,讓我們創(chuàng)建一個基本實體類:Entity,并在Id字段上生成Equals和GetHashCode。如果2個實體E1和E2具有相同的id,則 E1==E2應該返回true
DDD聚合是可以作為單個單元處理的領域?qū)ο蟮募骸@缬唵渭捌溆唵雾?#xff0c;它們將是單獨的對象,但將訂單(及其訂單項)視為單個聚合非常有用。
聚合應該始終處于有效狀態(tài),并且每個聚合都有一個根是一個實體,不屬于該聚合的類只能引用聚合根。
因此,讓我們創(chuàng)建一個繼承自Entity的基類AggregateRoot,我將其設為泛型,因為T是Id字段的類型,并且它可以根據(jù)這些實體而變化?
領域事件
領域事件通過避免直接調(diào)用來實現(xiàn)有界上下文之間的通信。所以一個有界上下文B1引發(fā)一個事件,一個或多個有界上下文B2…Bn對此事件的子訂閱方應該處理該事件以使用它。
因此,讓我們創(chuàng)建一個基類DomainEvent?
但是在這里,由于我實施事件溯源的策略,我的有界上下文產(chǎn)生的所有事件都將保存在我的事件存儲中。對這些事件感興趣的其他有界上下文、服務或其他程序?qū)⒈仨氂嗛喎湛偩€。
比如我每次創(chuàng)建一個新的Speech,然后我都會創(chuàng)建一個SpeechCreatedEvent事件
SpeechCreatedEvent類必須從DomainEvent基類繼承?
聚合根的最終實現(xiàn)將如下所示:?
因為Speech實體是聚合根,所以讓我們繼續(xù)從AggregateRoot繼承它,Speech實體的Id字段是一個 Guid
讓我們添加一些測試來覆蓋 domainEvents?
LogCorner.EduSync.Speech.Application和LogCorner.EduSync.Speech.Domain是100%的代碼覆蓋率?
歡迎關(guān)注我的個人公眾號”My IO“
總結(jié)
以上是生活随笔為你收集整理的基于事件驱动架构构建微服务第2部分:领域对象和业务规则的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Blazor 组件之间使用 EventC
- 下一篇: 谈谈为什么我们需要云原生架构?