整潔的領域驅動設計 - George

banq發表於2022-02-25

這篇文章將介紹一種使用DDDclean Architecture構建應用程式的觀點性方法。

我所說的 "觀點 "是指,我將論證解決應用程式設計和架構中幾個眾所周知的問題的特定方式。當然,這並不意味著這是實現這些問題的唯一正確方法。

然而,它是DDD和架構領域長期以來的研究過程和成果。

網上有許多關於DDD核心概念的帖子和問題:一個問題問的是聚合和業務邏輯的位置,而業務邏輯執行的是跨越幾個聚合的規則。

很多人似乎對一些事情很不清楚,比如準確的聚合邊界、聚合根、實體之間的導航(在聚合內部)、聚合持久化(有無ORM)以及處理跨聚合關注和操作的邏輯放在哪裡。這是可以理解的,這些確實是複雜的話題,非常依賴於問題領域的確切性質。

另一方面,在架構和設計領域也有一些有趣的發展,似乎是對DDD方法的補充。我在這裡說的是Clean Architecture和CQRS等方法。

因此,考慮到這些,我想知道我是否能想出一個相對簡單的方法,將這些方法中最優秀的想法結合起來,展示出一種建立強大的應用程式的方法,並且易於理解和維護。

 

案例

案例原始碼:GitHub repository.

它處理非常簡單的域:有兩個實體:Student和Course. 學生Student可以註冊一門或多門課程Course。每門課程都會跟蹤註冊的學生人數,每個學生都會跟蹤她註冊的課程。顯然,應用程式必須允許彼此獨立地建立和編輯新的Course或新的。Student但是,為學生註冊課程必須保證當且僅當學生成功註冊了她的課程註冊時,該課程的註冊學生數量才會相應增加。

儘管這個領域非常簡單,但它使我們能夠關注幾個重要問題。我們如何在聚合體和聚合根方面對這個領域進行建模?我們是否讓其中一個實體(例如,課程是具有依賴實體 "學生 "的聚合根?或者反之亦然?或者我們需要另一個(根)聚合,比如說包含其他兩個實體的 "入學"(Enrollment)?註冊的邏輯在哪裡,即:我們應該在哪裡以及如何執行我們關於課程的註冊學生數量的業務規則?

  

將重點從DDD轉向Clean模式

我提倡的方法要求將焦點從純 DDD 意義上的實體普遍轉移到 Robert C. Martin 在他非常著名的文章中描述的用例。

Clean架構中的用例與DDD服務概念?

  • 用例UseCase可以將任何輸出埠用於業務邏輯所需的任何操作。這在從持久層獲取實體時特別有用。在某種程度上,Clean 正規化中的用例UseCase是經典 DDD 意義上的應用程式服務和領域服務的組合。
  • 用例本質上是程式性的,但非常關注手頭的業務需求。這增加了程式碼的可理解性。用例也以非常具體的業務場景命名。
  • 我們可以完全控制在每個特定用例中要執行的方法的確切順序。
  • 用例級別的事務劃分確保在發生錯誤或異常的情況下,聚合的狀態將保持一致。
  • 遵循埠和介面卡(清潔)架構的原則,用例非常容易測試。

 

DTO、值物件和不可變實體

領域驅動設計要求廣泛使用諸如值物件和實體的戰術模式。在我們的應用中當然也是如此。

此外,我認為,"查詢 "型別的用例應該直接使用從持久層返回的DTOs,或者將這些DTOs對映到值物件。這是因為,這些物件是相關聚合體的幾個屬性的表示,註定要在UI層中用於非常具體的業務場景。

 

結論

總而言之,這篇文章提出了一種使用多種正規化混合構建應用程式的特定方式。該方法在此處找到的示例中進行了說明。以下是要點:

  • DDD 用於對封裝聚合內不變數(建構函式中的驗證器)的域實體進行建模。
  • 用例(來自 Clean Architecture)用於以可理解和有針對性的方式編排聚合間的業務邏輯。
  • 用例級別的事務劃分允許執行非聚合規則。
  • 從 CQRS 正規化借用的方法允許從“命令”型別的用例中有效地將 ORM 用於 C(r)RUD 操作,而簡單的 JDBC 查詢(帶有 SQL“JOIN”)用於需要訪問的“查詢”型別的用例到幾個相關的聚合。

更多點選標題見原文。

 

相關文章