當前位置: 妍妍網 > 碼農

兩萬字淺談 DDD 領域驅動設計

2024-02-24碼農

來源|juejin.cn/post/7252860409613942842

👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 贈書福利

全棧前後端分離部落格計畫 1.0 版本完結啦,2.0 正在更新中 ... , 演示連結 http://116.62.199.48/ ,全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了219小節,累計34w+字,講解圖:1492張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,

一、引言

軟體開發中的挑戰和問題

  1. 復雜性管理: 當處理復雜業務需求時,軟體系統往往變得復雜,難以理解和維護。不清晰的業務邏輯和模型使開發人員難以捕捉並準確地實作業務需求。

  2. 領域專家與開發人員之間的溝通障礙: 業務專家負責提供業務需求和知識,而開發人員負責將這些需求轉化為可執行的軟體系統。然而,由於不同的專業背景和術語之間的差異,很難進行有效的溝通,造成開發過程中的誤解和偏差。

  3. 資料庫驅動設計的局限性: 在傳統的軟體開發中,往往將資料庫設計作為業務邏輯的中心。這導致了緊密耦合的數據模型和業務邏輯,使系統變得脆弱且難以修改和擴充套件。

  4. 難以應對變化: 在現實世界中,業務需求會不斷變化和演化。然而,傳統的軟體開發方法往往缺乏靈活性,難以適應這種變化。系統修改和擴充套件常常會引入錯誤和破壞現有的結構。

DDD 架構的定義和目標

當談到領域驅動設計(Domain-Driven Design,DDD)架構時,它是一種軟體設計方法,旨在幫助開發人員更好地理解和解決復雜業務領域的挑戰。DDD 架構的目標是將軟體設計與實際業務需求緊密結合,透過明確的領域模型和業務概念來支持系統的開發和演化。

1.定義:

領域驅動設計是一種基於領域模型的軟體設計和開發方法,強調將軟體設計與業務領域的實際需求相結合。它提供了一組原則、模式和工具,幫助團隊更好地理解業務領域、捕捉業務知識,並以清晰的方式將其對映到軟體系統中。

2.目標:
  • 解決復雜性: DDD 透過將業務領域劃分為明確的模組和概念,幫助開發人員處理復雜性。它鼓勵建立一個明確的、可靠的領域模型,幫助開發人員更好地理解和應對業務領域的挑戰,從而簡化開發過程。

  • 清晰的業務模型: DDD 強調提取和表達業務知識,並將其對映到軟體系統中的領域模型。透過建立一個明確的、統一的領域模型,團隊成員可以共享對業務概念和規則的理解,促進更好的溝通和對業務需求的一致性理解。

  • 高度可維護性: DDD 倡導使用清晰的領域模型來構建軟體系統,這有助於提高系統的可維護性。透過將業務邏輯和狀態封裝在領域物件中,並使用聚合根等DDD模式,可以簡化程式碼結構,降低耦合性,從而使系統更易於修改和擴充套件。

  • 叠代開發和增量交付: DDD 鼓勵采用增量開發和敏捷方法,透過叠代方式逐步完善和驗證領域模型。它強調與領域專家密切合作,透過快速叠代的方式逐步演化系統,以滿足不斷變化的業務需求。

  • 技術和業務的融合: DDD 鼓勵技術人員和業務專家之間的緊密合作,透過共同理解和共享語言來構建一個有效的領域模型。它試圖消除技術術語和業務術語之間的隔閡,促進團隊之間的有效溝通和協作。

  • 透過遵循 DDD 架構的原則和模式,開發人員可以更好地理解和解決復雜業務需求,構建可維護、高度設計的軟體系統,並與業務專家進行更緊密的合作。這種方法有助於確保軟體系統與實際業務需求的一致性,提高開發效率並最大程度地滿足使用者需求。

    DDD 架構的重要性和套用場景

    DDD(領域驅動設計)架構的重要性在於它提供了一種將軟體系統的復雜業務邏輯與技術實作相結合的方法。它強調以領域模型為核心,透過深入理解和準確對映業務領域,來解決傳統開發中的一些常見問題,提高軟體系統的可維護性、可延伸性和靈活性。

    以下是 DDD 架構的重要性和套用場景的詳細介紹:

    1. 業務復雜性管理: 軟體系統往往涉及復雜的業務需求和邏輯。DDD 提供了一種將復雜業務邏輯進行建模和組織的方法,透過領域模型的概念和規則,使開發人員能夠更好地理解和處理復雜性,降低系統的認知負擔。

    2. 高效的溝通和協作: DDD 強調業務專家與開發人員之間的緊密合作。透過共同建立和維護領域模型,業務專家能夠更有效地表達需求和規則,開發人員可以更準確地理解和實作這些需求。這種良好的溝通和協作有助於減少開發過程中的誤解和偏差,提高開發效率和品質。

    3. 高內聚、低耦合的模組化設計: DDD 透過將軟體系統劃分為多個領域模型和限界上下文,強調模組化和邊界的概念。每個模組都有自己的職責和規則,模組之間透過清晰的介面進行互動,從而實作高內聚、低耦合的設計。這種模組化的設計使系統更易於理解、修改和擴充套件,提高了系統的可維護性和靈活性。

    4. 支持變化和演化: DDD 提倡對業務需求的變化持開放態度,並提供了適應變化的方法。透過領域模型的概念,DDD 強調將業務邏輯和規則封裝在模型中,使其更易於修改和演化。當業務需求發生變化時,可以透過調整模型而不是整個系統來適應變化,減少對系統的影響。

    5. 提高軟體品質: DDD 強調關註業務領域本身而非技術細節,幫助開發人員更好地理解業務需求。透過準確對映業務領域,可以更容易地驗證系統的正確性和完整性。同時,DDD 還鼓勵使用領域驅動測試來驗證領域模型的行為,確保系統按照預期工作。

    在實際套用中,DDD 適用於以下場景:

    1. 復雜業務系統: 當開發的軟體系統涉及復雜的業務需求和邏輯時,DDD 可以幫助將這些復雜性進行合理組織和管理。

    2. 長期維護和演化: 當軟體系統需要長期維護和演化時,DDD 的模組化設計和適應變化的特效能夠降低修改和擴充套件的風險。

    3. 多團隊協作: 當多個團隊同時開發一個大型軟體系統時,DDD 提供了明確的邊界和介面定義,有助於不同團隊之間的協作和整合。

    4. 高度可客製的業務需求: 當業務需求需要高度客製化和個人化時,DDD 的領域模型可以準確表達特定的業務規則和行為。

    二、DDD 架構的核心概念

    領域模型和領域物件的概念

    領域模型和領域物件是領域驅動設計(DDD)中的兩個核心概念,它們在軟體開發中起著重要的作用。

    1.領域模型(Domain Model):

    領域模型是對業務領域的抽象和建模,它描述了業務中的概念、規則和關系。領域模型是對現實世界的業務問題進行抽象的結果,它反映了業務專家對領域的理解,並將其表達為軟體系統中的物件和邏輯。領域模型通常由實體(Entities)、值物件(Value Objects)、聚合(Aggregates)、服務(Services)等組成。

    領域模型的設計旨在準確地反映業務領域的本質特征,並將其與技術實作相分離。透過領域模型,開發人員能夠更好地理解業務需求、規則和流程,提供一種共享的語言,促進開發團隊與業務專家之間的溝通與協作。

    2.領域物件(Domain Object):

    領域物件是領域模型中的具體實體,代表了業務領域中的一個概念或實體。它是領域模型中的核心元素,包含了數據和行為,並且具有業務規則和約束。領域物件通常具有唯一的標識,並透過標識來進行區分和操作。

    領域物件不僅包含了數據的狀態,還具有對這些數據進行操作和處理的方法。它封裝了業務行為和邏輯,實作了業務規則的驗證和執行。領域物件的設計應該註重領域的本質特征,準確表達業務需求,並透過方法的行為來保護和維護其內部數據的完整性和一致性。

    領域物件在領域模型中相互互動和協作,透過訊息傳遞和呼叫方法來實作業務流程和功能。它們可以形成聚合,建立關聯關系,參與業務規則的執行和數據的變更。

    聚合根和實體的定義和作用

    在領域驅動設計(DDD)中,聚合根(Aggregate Root)和實體(Entity)是用於建模領域模型的重要概念,它們具有不同的定義和作用。

    1.聚合根(Aggregate Root):

    聚合根是領域模型中的一個重要概念,它是一組相關物件的根節點,代表了一個整體的概念或實體。聚合根負責維護聚合內部的一致性和完整性,並提供對聚合內部物件的存取和操作。

    聚合根透過封裝內部的實體、值物件和關聯關系,形成一個邊界,它定義了聚合的邊界和存取規則。聚合根通常具有全域唯一的標識,可以透過該標識來標識和存取整個聚合。聚合根作為聚合的入口點,透過公開的方法來處理聚合內部的業務邏輯和行為。

    聚合根在領域模型中扮演著重要的角色,它具有以下作用:

  • 約束整個聚合的一致性和完整性

  • 提供對聚合內部物件的存取和操作

  • 封裝聚合內部的復雜關聯關系和業務規則

  • 作為聚合的介面,與外部系統進行互動

  • 2.實體(Entity):

    實體是領域模型中具體的物件,代表了業務領域中的一個具體概念或實體。實體具有唯一的標識,並且在整個系統中可以透過該標識進行辨識和存取。實體包含了數據和行為,並且具有業務規則和行為。

    實體通常屬於某個聚合,並且在聚合內部起到具體的角色。實體可以直接參與業務規則的驗證和執行,它負責維護自身的狀態和行為,並與其他實體進行互動。實體可以有自己的內容和方法,並且可以透過訊息傳遞和呼叫方法來實作與其他實體的協作和互動。

    實體在領域模型中扮演著以下作用:

  • 表示業務領域中的具體概念和物件

  • 維護自身的狀態和行為

  • 參與聚合內部的業務規則的執行和數據的變更

  • 與其他實體進行互動和協作

  • 總結起來,聚合根是領域模型中一組相關物件的根節點,它負責維護整個聚合的一致性和完整性;而實體是具體的業務概念和物件,代表了聚合內部的一個具體例項,它負責維護自身的狀態和行為,並與其他實體進行互動。聚合根和實體在領域模型中具有不同的定義和作用,它們協同工作,構建出強大而靈活的領域模型,提供了一種可靠的方法來處理復雜的業務需求。

    值物件和服務的概念

    1.值物件(Value Object):

    值物件是指在領域模型中用來表示某種特定值或內容的物件,它沒有唯一的識別元,透過其內容值來區分不同的物件。值物件通常被用於聚合內部,作為實體的內容或者組成聚合根的一部份。

    值物件具有以下特點:

  • 封裝重復的內容,提高程式碼可讀性和可維護性。

  • 提供了一種更加表達領域概念的方式,增強了程式碼的語意性。

  • 作為實體的內容,幫助實體建立復雜的關聯關系。

  • 支持領域行為的建模和封裝。

  • 值物件的作用:

  • 不可變性: 值物件的內容值在建立後不可改變,任何修改都應該建立一個新的值物件。

  • 相等性: 值物件的相等性根據其內容值來決定,相同內容值的值物件被認為是相等的。

  • 無副作用: 值物件的行為不會產生副作用,對其的操作不會改變系統的狀態。

  • 2.服務(Service):

    服務是領域模型中的一種行為抽象,它表示一組操作或行為的集合,通常與領域物件無關。服務可以是無狀態的,也可以是有狀態的,它們透過介面或者靜態方法來提供服務。

    服務具有以下特點:

  • 處理復雜的領域操作,協調多個實體或值物件之間的互動。

  • 提供領域無關的功能,例如驗證、計算等。

  • 支持領域模型的完整性和一致性。

  • 服務的作用:

  • 封裝行為: 服務封裝了一組操作或者行為,在方法級別上對領域操作進行了組織和歸納。

  • 強調整合: 服務跨越多個領域物件,協調它們之間的互動,促進領域模型的整體性和一致性。

  • 面向操作: 服務的主要目的是執行某個操作,並且不保留任何狀態。

  • 三、DDD 架構中的分層思想

    分層架構的概述和好處

    領域驅動設計(Domain-Driven Design,DDD)分層架構是一種常用於構建復雜軟體系統的架構風格。它將系統劃分為多個層次,每個層次都有特定的職責和關註點,以實作高內聚低耦合的目標。

    1.概述:

    DDD分層架構基於單一職責原則(Single Responsibility Principle)和依賴倒置原則(Dependency Inversion Principle)構建,提供了一種將業務邏輯、領域模型和基礎架構等不同關註點進行分離的方式。

    通常,DDD分層架構由以下幾個層次組成:

  • 使用者介面層(User Interface Layer): 負責與使用者互動,展現系統的使用者介面,接收使用者輸入和顯示輸出。

  • 套用層(Application Layer): 協調使用者介面和領域層之間的互動,處理使用者請求,呼叫領域服務和領域物件來完成業務邏輯。

  • 領域層(Domain Layer): 包含領域模型、實體、值物件等,負責實作業務規則和行為,封裝核心的業務邏輯。

  • 基礎架構層(Infrastructure Layer): 提供與基礎設施相關的支持,包括資料庫存取、訊息佇列、日誌等。

  • 2.好處:

    DDD分層架構帶來了以下好處:

  • 高內聚低耦合: 透過將不同關註點分離到不同層中,實作了高內聚和低耦合。每個層次都有明確的職責,可以更容易理解和維護。

  • 可測試性: 每個層次可以獨立測試,因為它們的職責清晰,依賴關系明確。這樣可以更容易編寫單元測試和整合測試,提高程式碼品質。

  • 可延伸性: 由於各層之間的松散耦合,當需要添加新功能或修改現有功能時,只需修改特定的層次,而無需影響其他層次。

  • 可維護性: DDD分層架構使得系統的各個部份有明確的職責和邊界,降低了程式碼的復雜性,提高了程式碼的可讀性和可維護性。

  • 模組化開發: 不同層次之間的分離使得開發團隊可以更好地並列工作,各自專註於自己的任務,提高開發效率。

  • 要註意的是,DDD分層架構並不是一成不變的,具體的架構設計可能因系統規模、團隊結構和業務需求等因素而有所調整。重要的是理解各層次的職責和關註點,並保持良好的程式碼組織和架構設計原則,以實作可維護、可延伸的軟體系統。

    領域層、套用層和基礎設施層的職責和關系

    1.領域層:

    職責: 領域層是整個系統的核心,負責實作業務規則和邏輯。它包含了領域模型、實體、值物件、聚合根等概念。領域層主要完成以下職責:

  • 實作業務領域的概念和規則。

  • 封裝核心的業務邏輯。

  • 管理領域物件之間的關系和互動。

  • 保證數據的一致性和有效性。

  • 關系: 領域層通常不依賴於其他層,並且其他層也不應該直接依賴於領域層。它透過定義介面或領域服務的方式暴露給套用層,套用層可以呼叫領域層的介面或服務來處理業務邏輯。

    2.套用層:

    職責: 套用層作為領域層和使用者介面層之間的協調者,負責處理使用者請求、協調領域物件和領域服務來完成業務邏輯。套用層主要完成以下職責:

  • 接收和驗證使用者輸入。

  • 轉化使用者請求為領域物件的操作。

  • 協調多個領域物件之間的互動和協作。

  • 呼叫領域服務來完成復雜的業務操作。

  • 關系: 套用層依賴於領域層,透過呼叫領域層中的介面或領域服務來實作業務邏輯。它還可以呼叫基礎設施層提供的服務來處理與外部系統的互動。

    3.基礎設施層:

    職責: 基礎設施層提供與基礎設施相關的支持,包括資料庫存取、訊息佇列、外部API呼叫、緩存、日誌等功能。基礎設施層主要完成以下職責:

  • 與外部系統進行通訊和互動。

  • 提供數據持久化的支持,例如資料庫存取、ORM等。

  • 實作與基礎設施相關的技術細節,例如日誌記錄、緩存管理等。

  • 關系: 基礎設施層依賴於套用層和領域層,它為這些層提供必要的支持和服務。例如,領域層和套用層可以透過基礎設施層存取資料庫、記錄日誌或發送訊息。

    總體而言,領域層關註業務邏輯和規則,套用層協調業務邏輯的執行,基礎設施層提供系統級的技術支持。它們之間的關系是領域層不依賴其他層,套用層依賴領域層,基礎設施層提供支持給套用層和領域層。

    領域事件和領域服務的使用

    1. 領域事件:

    定義: 領域事件是指在領域模型中發生的具有業務意義的、可追溯的事情或狀態變化。它通常表示系統中重要的業務行為或關鍵的業務狀態轉換。

    使用場景: 使用領域事件可以捕捉和記錄領域模型中的重要業務行為,以便在該事件發生後執行相應的業務邏輯。一些常見的使用場景包括:

  • 觸發其他領域物件的行為或狀態變化。

  • 提供領域物件之間的解耦,使得系統更加靈活和可延伸。

  • 記錄和跟蹤系統的狀態變化,以滿足審計需求。

  • 與外部系統進行異步通訊,例如釋出訊息給訊息佇列。

  • 實作方式: 領域事件通常由領域物件產生並釋出,可以使用觀察者模式或釋出-訂閱模式進行訂閱和處理。在事件釋出時,套用層或基礎設施層可以監聽並執行相應的業務邏輯。

    2. 領域服務:

    定義: 領域服務是指在領域模型中提供的一種操作或功能,它不屬於任何特定的領域物件,而是用於協調多個領域物件之間的互動和實作復雜的業務邏輯。

    使用場景: 使用領域服務可以處理那些涉及多個領域物件或需要跨領域邊界的業務操作。一些常見的使用場景包括:

  • 處理涉及多個領域物件的復雜業務邏輯。

  • 跨聚合根或子體執行事務性操作。

  • 與外部系統進行互動或整合。

  • 實作方式: 領域服務通常被定義為領域模型中的一個介面或抽象類,並由具體的實作類來提供具體的操作。在套用層中,可以透過呼叫領域服務的方法來執行相應的業務邏輯。

    四、DDD 架構中的設計原則和模式

    通用領域設計原則的介紹

    1. 領域模型貧血 VS 充血模型:
  • 領域模型貧血(Anemic Domain Model)是指將業務邏輯主要放在服務物件中,而領域物件(實體、值物件等)則只具備數據和基本操作,缺乏自身的行為。這種模型會導致業務邏輯分散和難以維護。

  • 充血模型(Rich Domain Model)是指將業務邏輯盡可能地放在領域物件中,使其具備自身的行為和狀態。這種模型可以更好地保護業務規則,提高內聚性和可維護性。

  • 2. 聚合根:
  • 聚合根(Aggregate Root)是領域模型的核心,它是一組具有內聚性的相關物件的根實體。聚合根負責維護整個聚合內的一致性和業務規則。

  • 透過定義聚合根,可以避免直接操作聚合內的物件,而是透過聚合根進行操作,確保聚合的完整性、一致性和封裝性。

  • 3. 領域事件驅動:
  • 領域事件(Domain Event)是領域模型中重要的業務行為或狀態變化的表示。透過使用領域事件,可以實作領域物件之間的解耦和松散耦合。

  • 領域事件的發生可以觸發其他領域物件的行為或狀態變化,實作業務流程的演進和響應。

  • 4. 值物件:
  • 值物件(Value Object)是指沒有唯一標識、不可變且沒有生命周期的物件。它們通常用來表示領域中的某個值或內容,並且可以作為實體的內容或參數。

  • 值物件應該透過封裝自身的狀態來保證數據的一致性和完整性,提供相等性判斷和對外不可變等特性。

  • 5. 領域服務:
  • 領域服務(Domain Service)是指不屬於任何特定的領域物件,而是用於解決跨多個領域物件的復雜業務邏輯的操作或功能。

  • 領域服務可以協調多個領域物件之間的互動,處理復雜的業務規則和操作,並實作領域模型中無法被單個領域物件所包含的業務。

  • 實體-值物件模式的使用

    DDD(領域驅動設計)中的實體-值物件模式是一種常用的領域建模技術,用於表示和處理領域中的實體和值物件。

    1. 實體(Entity):
  • 實體是具有唯一標識的具體領域物件,它具有生命周期、可以擁有狀態和行為,並且可以與其他實體進行互動。

  • 實體透過標識內容來區分不同的物件,並且可以根據業務規則進行狀態變化和行為操作。

  • 實體通常具有永續性,需要被保存到資料庫或其他儲存介質中。

  • 2. 值物件(Value Object):
  • 值物件是用於描述某個特定概念的不可變物件,沒有唯一標識和生命周期,僅僅透過其內容值來區分不同的物件。

  • 值物件重點關註其內容的語意,而不是標識,因此通常具有更精簡的行為,只包含基本的存取方法。

  • 值物件一般不具有永續性,也不需要被單獨保存到資料庫中,它通常作為實體的組成部份存在。

  • 在使用實體-值物件模式時,以下是一些常見的註意事項和使用方式:

    1.實體的特點和使用:

  • 實體應該具有唯一標識,透過標識來區分不同的物件。

  • 實體的行為和狀態應該與其標識關聯,盡可能在實體內部處理業務規則和狀態變化。

  • 實體之間可以透過參照和關聯進行互動。

  • 2.值物件的特點和使用:

  • 值物件沒有唯一標識,僅透過其內容值來區分不同的物件。

  • 值物件應該是不可變的,即建立後不可修改其內容值。

  • 值物件可以作為實體的內容或參數來使用,用於描述實體的某個特定概念。

  • 3.實體和值物件的區別和選擇:

  • 實體是具有生命周期和標識的,適合表示具有獨立生命周期和狀態變化的物件。

  • 值物件是沒有生命周期和標識的,適合表示不可變的、相對簡單的概念。

  • 在建模過程中,根據具體的業務需求和概念的本質,選擇合適的實體或值物件來表示。

  • 4.實體和值物件的關系:

  • 實體可以包含值物件作為其內容,用於描述實體的某些內容或特征。

  • 實體和值物件之間可以存在聚合關系,即實體可以作為聚合根,擁有一組相關的實體和值物件。

  • 以下是一個簡單的Java程式碼範例,展示了實體和值物件的使用:

    // 實體 - User
    public classUser{
    private UUID id;
    private String username;
    private String password;
    publicUser(UUID id, String username, String password){
    this.id = id;
    this.username = username;
    this.password = password;
    }
    // Getters and setters
    // ...
    publicbooleanisValidPassword(String inputPassword){
    returnthis.password.equals(inputPassword);
    }
    }
    // 值物件 - Address
    public classAddress{
    private String street;
    private String city;
    private String country;
    publicAddress(String street, String city, String country){
    this.street = street;
    this.city = city;
    this.country = country;
    }
    // Getters and setters
    // ...
    }
    // 使用實體和值物件
    public classMain{
    publicstaticvoidmain(String[] args){
    UUID userId = UUID.randomUUID();
    User user = new User(userId, "johnDoe""password123");
    Address address = new Address("123 Main St""Cityville""Countryland");
    user.setAddress(address);
    // 存取實體和值物件的內容
    System.out.println("User ID: " + user.getId());
    System.out.println("Username: " + user.getUsername());
    Address userAddress = user.getAddress();
    System.out.println("Street: " + userAddress.getStreet());
    System.out.println("City: " + userAddress.getCity());
    System.out.println("Country: " + userAddress.getCountry());
    // 呼叫實體的方法
    String inputPassword = "password123";
    boolean isValid = user.isValidPassword(inputPassword);
    System.out.println("Is valid password? " + isValid);
    }
    }










    以上範例中,定義了一個 User 實體類和一個 Address 值物件類。 User 具有唯一標識(UUID)、使用者名稱和密碼內容,並且有一個 isValidPassword 方法用於驗證密碼的有效性。 Address 作為 User 的一個內容,描述了使用者的地址。

    Main 類中,建立了一個 User 物件和一個 Address 物件,並透過呼叫相應的setter方法將 Address 物件賦值給 User 物件。然後透過存取實體和值物件的內容來打印相關資訊,並呼叫實體的方法進行驗證。

    聚合根和聚合模式的套用

    DDD(領域驅動設計)中的聚合根和聚合模式是一種重要的設計概念,用於管理和維護領域物件之間的整體性和一致性。

    1. 聚合根(Aggregate Root):
  • 聚合根是領域模型中的一個重要概念,它是一組相關物件的根實體,代表了一個聚合的邊界。

  • 聚合根必須負責保證聚合內物件的一致性和完整性,並提供對聚合內物件的存取、操作和維護。

  • 聚合根透過標識內容來唯一標識一個聚合例項,並將聚合內部的物件封裝在其內部。

  • 聚合根應該盡可能減少外部對聚合內部物件的直接存取,而透過聚合根提供的方法來間接操作聚合內部物件。

  • 2. 聚合模式(Aggregate Pattern):
  • 聚合模式是一種將一組相關的領域物件組織成聚合的方式,以實作整體性和一致性的管理。

  • 聚合由聚合根和聚合內的物件組成,聚合根負責管理和控制聚合內的物件,維護其狀態一致性。

  • 聚合模式透過將領域物件劃分為聚合根、實體和值物件等階層,以及定義聚合根的邊界和關聯關系,來約束物件之間的存取和操作。

  • 聚合模式強調封裝和隱藏聚合內部物件,透過聚合根提供的方法來管理聚合的行為和狀態。

  • 在套用聚合根和聚合模式時,以下是一些常見的使用場景和註意事項:

    1. 聚合根的定義和使用:
  • 聚合根應該是具有唯一標識的實體物件,代表了整個聚合的邊界。

  • 聚合根負責管理和維護聚合內的物件之間的關系和一致性。

  • 外部物件應該透過聚合根來存取和操作聚合內的物件,以保證聚合的整體性。

  • 2. 聚合內部物件的定義和存取:
  • 聚合內部的物件可以包括實體、值物件和其他聚合根,根據具體業務需求來設計和劃分。

  • 聚合內部物件應該被封裝起來,不直接暴露給外部物件。

  • 外部物件透過聚合根提供的方法來間接存取和操作聚合內部的物件。

  • 3. 聚合之間的關系和邊界:
  • 聚合之間可以存在巢狀和參照關系,形成復雜的領域模型網。

  • 每個聚合根應該有清晰的邊界,以限制不同聚合之間的直接存取和操作。

  • 聚合之間的關系應該透過聚合根的標識內容和參照內容來建立。

  • 4. 事務邊界和持久化:
  • DDD中的聚合通常對應著資料庫事務的邊界,一個聚合就是一個單元的數據修改和持久化操作。

  • 聚合根負責控制和維護聚合內物件的一致性,確保整個聚合的狀態在事務內是一致的。

  • 聚合根的持久化應該與聚合內部的物件一起進行,保證數據的完整性和一致性。

  • 聚合根和聚合模式的套用可以提高領域建模的粒度和靈活性,將領域物件組織為聚合能夠更好地管理物件間的關系和狀態變化。使用聚合根和聚合模式能夠提高系統的可維護性、可延伸性和並行性,並減少領域模型的復雜度。

    以下是一個簡單的Java程式碼範例,用於說明聚合根和聚合模式的套用方式:

    // 聚合根類
    public classOrderAggregate{
    private String orderId;
    private List<OrderItem> orderItems;
    publicOrderAggregate(String orderId){
    this.orderId = orderId;
    this.orderItems = new ArrayList<>();
    }
    // 添加訂單項
    publicvoidaddOrderItem(String productId, int quantity){
    OrderItem item = new OrderItem(productId, quantity);
    orderItems.add(item);
    }
    // 移除訂單項
    publicvoidremoveOrderItem(String productId){
    OrderItem itemToRemove = null;
    for (OrderItem item : orderItems) {
    if (item.getProductId().equals(productId)) {
    itemToRemove = item;
    break;
    }
    }
    if (itemToRemove != null) {
    orderItems.remove(itemToRemove);
    }
    }
    // 獲取所有訂單項
    public List<OrderItem> getOrderItems(){
    return orderItems;
    }
    // 計算訂單總價
    publicdoublecalculateTotalPrice(){
    double totalPrice = 0.0;
    for (OrderItem item : orderItems) {
    double itemPrice = item.calculateItemPrice();
    totalPrice += itemPrice;
    }
    return totalPrice;
    }
    }
    // 訂單項類
    public classOrderItem{
    private String productId;
    privateint quantity;
    publicOrderItem(String productId, int quantity){
    this.productId = productId;
    this.quantity = quantity;
    }
    // 獲取產品ID
    public String getProductId(){
    return productId;
    }
    // 獲取訂單項數量
    publicintgetQuantity(){
    return quantity;
    }
    // 計算訂單項總價
    publicdoublecalculateItemPrice(){
    // 根據產品ID和數量計算訂單項的總價,這裏只是個範例,具體實作可以根據業務需求編寫
    return0.0;
    }
    }
    // 測試程式碼
    public classMain{
    publicstaticvoidmain(String[] args){
    // 建立聚合根物件
    OrderAggregate order = new OrderAggregate("123456");
    // 添加訂單項
    order.addOrderItem("001"2);
    order.addOrderItem("002"1);
    // 計算訂單總價
    double totalPrice = order.calculateTotalPrice();
    // 打印訂單總價
    System.out.println("Total Price: " + totalPrice);
    }
    }












    以上範例程式碼展示了一個簡單的訂單聚合,其中 OrderAggregate 表示聚合根, OrderItem 表示聚合內的物件。在 OrderAggregate 中,我們可以透過添加和移除訂單項來管理聚合內部的物件,並透過計算訂單總價方法來操作聚合內的物件。透過使用聚合根和聚合模式,我們能夠更好地組織和管理領域物件,提高程式碼的可維護性和擴充套件性。

    領域事件和事件驅動模式的實踐

    1. 領域事件:
  • 領域事件是在領域模型中發生的具有業務含義的事情,它記錄了一些狀態改變或者領域物件之間的互動。

  • 領域事件透過描述事情的發生來反映業務的變化,通常使用過去式的動詞來命名,如OrderCreated、ProductStockUpdated等。

  • 領域事件可以被釋出和訂閱,以便通知其他感興趣的領域模型或元件進行相應的處理。

  • 2. 事件驅動模式:
  • 事件驅動模式是一種軟體架構模式,其中系統的行為和狀態變化是由觸發的事件驅動的。

  • 在DDD中,事件驅動模式用於實作領域模型之間的解耦,透過釋出和訂閱事件來實作模組之間的通訊和協作。

  • 事件驅動模式采用訊息傳遞的方式,領域模型之間透過釋出事件和訂閱事件來進行通訊。

  • 釋出者負責釋出事件,訂閱者透過註冊自己的事件處理方法來訂閱感興趣的事件,並在事件發生時執行相應的響應邏輯。

  • 在實踐領域事件和事件驅動模式時,以下是一些常見的步驟和註意事項:

    1. 定義領域事件:
  • 根據業務需求和領域模型的變化,辨識和定義需要引入的領域事件。

  • 給每個領域事件命名,使用過去式的動詞來描述事件發生的動作。

  • 2. 實作領域事件:
  • 在領域模型中定義並觸發相應的領域事件,通常是在領域物件的行為方法中進行觸發。

  • 領域事件可以攜帶一些必要的數據資訊,以提供事件處理的上下文和業務參數。

  • 3. 事件釋出和訂閱機制:
  • 實作一個事件釋出和訂閱的機制,用於將事件傳遞給感興趣的訂閱者。可以使用訊息佇列、事件匯流排等技術來實作。

  • 訂閱者透過註冊事件處理方法來訂閱感興趣的事件,釋出者在事件發生時將事件發送給所有訂閱者。

  • 4. 事件處理:
  • 訂閱者實作相應的事件處理方法,用於接收和處理事件。處理方法根據事件的型別和數據執行相應的業務邏輯。

  • 事件處理方法可以修改自身狀態,呼叫其他領域模型的方法,發送新的命令等。

  • 5. 事件溯源和持久化:
  • 可以使用事件溯源機制來記錄和儲存所有發生的領域事件,以便進行回溯、重放和歷史數據分析。

  • 領域事件的持久化可以透過將事件儲存到事件日誌或資料庫中來實作,以保證事件的永續性和可靠性。

  • 透過實踐領域事件和事件驅動模式,可以實作領域模型之間的解耦、靈活性和可延伸性。事件驅動的方式可以幫助我們構建具有高內聚低耦合的領域模型,並支持系統的可伸縮性和可維護性。在設計和實作過程中,要根據具體業務需求和系統架構選擇合適的事件驅動技術和工具。

    以下是一個簡單的Java程式碼範例,演示了如何在領域模型中定義和使用領域事件:

    首先,我們定義一個領域事件類 OrderCreatedEvent ,用於表示訂單建立的事件:

    public classOrderCreatedEvent{
    privatefinal String orderId;
    privatefinal String customerName;
    publicOrderCreatedEvent(String orderId, String customerName){
    this.orderId = orderId;
    this.customerName = customerName;
    }
    public String getOrderId(){
    return orderId;
    }
    public String getCustomerName(){
    return customerName;
    }
    }

    接下來,我們定義一個訂單領域模型 Order ,其中包含了觸發和釋出領域事件的邏輯:

    import java.util.ArrayList;
    import java.util.List;
    public classOrder{
    privatefinal String orderId;
    privatefinal String customerName;
    privatefinal List<OrderCreatedEventListener> eventListeners;
    publicOrder(String orderId, String customerName){
    this.orderId = orderId;
    this.customerName = customerName;
    this.eventListeners = new ArrayList<>();
    }
    publicvoidaddEventListener(OrderCreatedEventListener listener){
    eventListeners.add(listener);
    }
    publicvoidcreate(){
    // 建立訂單的邏輯...
    // 釋出領域事件
    OrderCreatedEvent event = new OrderCreatedEvent(orderId, customerName);
    notifyEventListeners(event);
    }
    privatevoidnotifyEventListeners(OrderCreatedEvent event){
    for (OrderCreatedEventListener listener : eventListeners) {
    listener.onOrderCreated(event);
    }
    }
    }




    Order 類中,我們定義了一個 addEventListener 方法,用於訂閱訂單建立事件的監聽器。在 create 方法中,當訂單建立完成後,我們會觸發相應的領域事件,並通知所有的訂閱者。

    下面是一個訂單建立事件的監聽器介面 OrderCreatedEventListener 的定義:

    publicinterfaceOrderCreatedEventListener{
    voidonOrderCreated(OrderCreatedEvent event);
    }

    訂閱者可以實作 OrderCreatedEventListener 介面,並實作 onOrderCreated 方法來處理訂單建立事件。

    以下是一個訂閱者的範例實作:

    public classEmailNotificationServiceimplementsOrderCreatedEventListener{
    @Override
    publicvoidonOrderCreated(OrderCreatedEvent event){
    // 發送信件通知給顧客
    String orderId = event.getOrderId();
    String customerName = event.getCustomerName();
    System.out.println("發送信件通知:訂單 " + orderId + " 已建立,顧客名:" + customerName);
    }
    }

    在這個範例中, EmailNotificationService 實作了 OrderCreatedEventListener 介面,並在 onOrderCreated 方法中發送信件通知給顧客。

    最後,我們可以進行測試,使用以下程式碼建立一個訂單並觸發訂單建立事件:

    public classMain{
    publicstaticvoidmain(String[] args){
    // 建立訂單
    Order order = new Order("123456""John Doe");
    // 添加訂閱者
    EmailNotificationService notificationService = new EmailNotificationService();
    order.addEventListener(notificationService);
    // 觸發訂單建立事件
    order.create();
    }
    }

    當執行 order.create() 方法時,訂單建立事件將被觸發,並通知 EmailNotificationService 發送信件通知給顧客。

    五、DDD 架構的套用實踐

    領域驅動設計的計畫實施步驟

    1. 理解業務需求: 在開始實施DDD之前,首先需要充分理解業務需求和上下文。與業務專家合作,深入了解業務領域、業務規則、業務流程等方面的資訊。這有助於建立一個準確的領域模型以及對業務的全面理解。

    2. 劃定限界上下文: 透過定義限界上下文,將業務分成不同的子領域。限界上下文是一種邏輯邊界,用於定義和隔離每個子領域的範圍。每個限界上下文都與特定的業務功能或業務概念相關聯,並有自己的領域模型。

    3. 構建領域模型: 針對每個限界上下文,開始構建對應的領域模型。領域模型是對業務領域中的實體、值物件、聚合根、領域服務等各個領域概念的建模。使用領域語言,將業務規則和概念轉化為程式碼實體和物件。

    4. 強調領域模型的設計: 領域模型是DDD最核心的部份,需要註重其設計。透過使用領域驅動設計的原則和模式,如聚合、領域事件、領域服務等,來構建可維護、可延伸的領域模型。在設計過程中,可以使用UML類圖、領域事件風暴等工具來輔助設計。

    5. 實施戰術模式: DDD提供了一系列戰術模式用於解決常見的領域建模問題,如實體、值物件、聚合、倉儲、領域事件等。根據實際情況選擇合適的戰術模式來實作領域模型。

    6. 持續叠代開發: 在DDD計畫中,持續叠代開發是很重要的。將計畫劃分成多個小的叠代周期,每個周期都能完成一個或多個功能點的開發。透過叠代開發,逐漸完善領域模型並不斷與業務需求進行驗證和調整。

    7. 領域驅動的架構設計: DDD強調以領域模型為核心的架構設計。設計合適的架構模式,如六邊形架構、CQRS(Command and Query Responsibility Segregation)等,以支持領域模型的實作和演進。

    8. 領域事件驅動: 使用領域事件來解耦領域模型和外部系統之間的通訊。透過使用事件驅動架構,可以實作松耦合、可伸縮的系統,並支持分布式系統的開發。

    9. 測試與驗證: 在DDD計畫中,測試至關重要。編寫針對領域模型的單元測試、整合測試和端到端測試,確保領域模型的正確性和穩定性。此外,需要與業務專家進行溝通和驗證,確保領域模型符合業務需求。

    10. 持續最佳化: 隨著計畫的進行,不斷根據反饋和實際執行情況對領域模型進行最佳化和演進。根據計畫的實際需求,可能需要對領域模型、限界上下文的劃分、架構設計等進行調整和最佳化。

    DDD 架構的架構設計和模組劃分

    1.領域層 (Domain Layer): 領域層是 DDD 的核心,包含了對業務領域的建模和實作。在該層中,將關註點放在業務核心概念、規則和邏輯上。主要包括以下模組:

  • 實體 (Entity): 代表領域中的具體物件,具有唯一的識別元和狀態。實體封裝了業務行為和狀態變化的邏輯。

  • 值物件 (Value Object): 在領域層中用於描述某個特定概念的不可變物件。值物件沒有唯一識別元,通常用於表示內容集合。

  • 聚合根 (Aggregate Root): 聚合是一組關聯物件的集合,聚合根是聚合中的一個主要物件。聚合根負責保證聚合內部的一致性和約束。

  • 領域服務 (Domain Service): 處理領域物件之間的復雜業務邏輯,不屬於任何一個具體的實體或值物件。

  • 領域事件 (Domain Event): 表示在領域中發生的重要事實,用於捕獲和傳遞領域內發生的變化。

  • 2.套用層 (Application Layer): 套用層處理使用者介面與領域層之間的互動,並協調不同的領域物件完成具體的套用需求。它負責接收使用者輸入,解析請求,呼叫領域物件的方法,並將結果返回給使用者。主要包括以下模組:

  • 套用服務 (Application Service): 提供使用者能直接呼叫的介面,負責接收使用者請求、處理輸入驗證和例外處理,協調領域物件的協作。

  • 套用事件 (Application Event): 用於在套用層中傳播資訊或通知,比如通知其他部份某個特定的事件已經發生。

  • 3.基礎設施層 (Infrastructure Layer): 基礎設施層提供支持整個應用程式執行的基礎設施服務,與具體的技術平台和框架相關。主要包括以下模組:

  • 持久化層 (Persistence Layer): 處理數據的持久化和存取,比如資料庫存取、ORM 對映等。

  • 外部服務層 (External Service Layer): 與外部系統進行互動的服務,比如第三方 API、訊息佇列、檔儲存等。

  • UI 層 (User Interface Layer): 處理使用者介面的實作,包括 Web 頁面、行動應用或其他使用者介面。

  • 4.共享內核 (Shared Kernel): 共享內核是一種可選的模組,用於處理多個子體之間共享的通用領域知識和元件。如果多個子體之間存在類似的業務邏輯或概念,可以將其抽象為共享內核,在不同的子體中共享和重用。

    這些層次和模組之間透過嚴格的邊界劃分和通訊機制來保持松耦合。領域層是 DDD 的核心,並且在架構設計中占據主導地位,因為它直接與業務領域相關。套用層負責協調和轉換使用者請求和領域物件之間的互動。基礎設施層提供了支持整個應用程式的基礎設施服務。共享內核用於處理多個子體之間的通用業務部份。

    DDD 架構與微服務架構的結合

    DDD(領域驅動設計)架構和微服務架構可以結合起來,以實作更靈活、可延伸和高內聚的系統設計。

    1. 微服務邊界與子體邊界對應: DDD 強調將復雜業務領域分解為子體,而微服務架構則將系統分解為一組小型自治服務。這兩者都強調透過明確的邊界來隔離和解耦各個功能模組。在結合時,可以將每個微服務與一個或多個子體對應起來,使每個微服務專註於特定的業務能力。

    2. 微服務作為套用層: 在 DDD 中,套用層負責協調使用者介面和領域層之間的互動。在微服務架構中,每個微服務都可以看作是一個獨立的套用。因此,可以將微服務作為套用層來實作,負責處理使用者請求、數據驗證、事務處理等工作,並協調呼叫領域層的功能。

    3. 領域驅動設計在微服務中的體現: DDD 的核心概念如實體、值物件、聚合根和領域服務等,可以對映到微服務的實作中。每個微服務可以有自己的領域模型,負責實作特定子體的業務邏輯。微服務之間可以透過領域事件進行異步通訊,以捕獲和傳遞領域內發生的變化。

    4. 微服務的自治性和松耦合性: 微服務架構鼓勵每個服務的自治性,即每個服務可以獨立開發、部署和擴充套件。在結合 DDD 時,每個微服務可以擁有自己的領域物件和業務規則,並透過領域事件或異步訊息佇列實作與其他微服務的解耦。這種松耦合性使得單個微服務的修改不會波及整個系統,提高了系統的可維護性和可延伸性。

    5. 按業務能力組織微服務: 在 DDD 中,子體是按業務能力進行劃分的,而微服務架構也強調遵循單一職責原則。因此,可以根據子體的邊界和業務能力來組織微服務,每個微服務專註於一個或多個相關的業務能力。

    6. 分布式數據管理: 微服務架構中,每個微服務都有自己的資料庫或數據儲存。在結合 DDD 時,每個微服務可以有自己的數據模型和數據存取層,負責管理自己的數據。需要註意確保數據的一致性和完整性,可以使用事件溯源或分布式事務等機制來處理跨微服務的數據一致性問題。

    六、DDD 架構的挑戰和解決方案

    復雜性和團隊協作的問題

    復雜性和團隊協作是軟體開發中普遍面臨的挑戰,尤其在大型計畫中更為突出。

    1. 復雜性問題:
  • 難以理解和管理: 大規模軟體系統通常涉及多個子系統和模組,其互動復雜度很高。程式碼的可讀性和可維護性受到挑戰。

  • 高度耦合和依賴: 不良的設計和實作可能導致元件之間緊密耦合,修改一個模組可能會影響其他模組,導致維護和擴充套件困難。

  • 技術棧和工具的選擇: 需要評估和選擇適合計畫需求的技術棧和工具,使得系統開發過程更加高效和可維護。

  • 2. 團隊協作問題:
  • 溝通和協調: 在大型計畫中,多個團隊成員之間的協作和溝通變得更加困難,需確保有效的資訊共享和溝通渠道。

  • 角色和責任: 明確團隊成員的角色和責任,確保每個人知道自己的任務和目標,避免任務沖突和重復工作。

  • 分布式團隊: 如果團隊分散在不同地區或時區,協作可能會更具挑戰性。需要采用合適的工具和流程來促進遠端團隊成員之間的協作和溝通。

  • 應對復雜性和團隊協作問題的方法包括:

    1. 使用適當的架構和設計模式: 透過使用合適的架構和設計模式,可以將系統分解為更小的、可管理的元件,並減少模組之間的耦合度。

    2. 引入自動化測試和持續整合: 使用自動化測試和持續整合工具,確保程式碼品質和系統穩定性,並及早發現和解決問題。

    3. 實踐敏捷開發方法: 采用敏捷開發方法,如Scrum或Kanban,以增加透明度、靈活性和反饋,促進團隊合作和快速響應變化。

    4. 建立清晰的溝通渠道: 確保團隊成員之間有良好的溝通和資訊共享機制,例如定期的會議、溝通工具和文件分享。

    5. 高效的計畫管理: 使用適當的計畫管理工具和技術,跟蹤任務進度、資源分配和風險管理,以確保計畫按時交付和團隊協作高效。

    6. 持續學習和知識分享: 鼓勵團隊成員進行持續學習,透過內部培訓、技術分享會等方式提高技能水平胡創用CC。

    面向領域的測試和自動化測試策略

    1. DDD 面向領域的測試策略:
  • 業務規則測試: 測試業務規則是否按預期工作,涉及驗證各種業務規則的正確性。

  • 聚合根測試: 聚合根是 DDD 中的核心概念,測試應確保聚合根的行為和狀態正確,並與其關聯的實體、值物件及領域事件進行適當的互動和更新。

  • 領域服務測試: 領域服務負責處理領域中的復雜業務邏輯,測試要驗證領域服務能夠正確執行其職責。

  • 領域事件測試: 測試領域事件的產生、釋出和處理,確保領域事件在系統中正確地傳播和觸發相關操作。

  • 領域模型一致性測試: 驗證領域模型的一致性和完整性,確保模型在各種場景下的正確行為。

  • 2. 自動化測試策略:
  • 單元測試: 透過編寫單元測試用例,測試領域物件、聚合根、值物件等的行為和狀態,並確保它們按預期工作。

  • 整合測試: 測試多個領域物件或服務之間的整合,驗證它們在協同工作時的正確性。

  • 介面測試: 測試與外部系統或服務的介面互動,確保數據傳輸和通訊的準確性和穩定性。

  • 持續整合測試: 將自動化測試納入持續整合流程,確保每次程式碼送出後都能進行自動化測試,從而及早發現問題並減少回歸測試的工作量。

  • 在進行 DDD 面向領域的測試和自動化測試時,需要關註以下註意事項:

  • 確保測試覆蓋率: 盡可能涵蓋業務領域的各個方面,以減少遺漏的測試場景。

  • 使用適當的測試框架和工具: 選擇適合領域驅動設計的測試框架和工具,例如基於 BDD(行為驅動開發)的測試框架,如Cucumber或SpecFlow。

  • 註重測試數據: 測試數據在 DDD 測試中至關重要,應該考慮各種不同的情況,包括邊界條件、異常情況等。

  • 與領域專家緊密合作: 與業務領域專家進行密切的合作和溝通,以確保測試場景和用例與業務需求一致。

  • 持續改進: 根據測試結果和反饋不斷改進測試策略和自動化測試框架,提高測試品質和效率。

  • 👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 贈書福利

    全棧前後端分離部落格計畫 1.0 版本完結啦,2.0 正在更新中 ... , 演示連結 http://116.62.199.48/ ,全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了219小節,累計34w+字,講解圖:1492張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,


    1. 

    2. 

    3. 

    4. 

    最近面試BAT,整理一份面試資料Java面試BATJ通關手冊,覆蓋了Java核心技術、JVM、Java並行、SSM、微服務、資料庫、數據結構等等。

    獲取方式:點「在看」,關註公眾號並回復 Java 領取,更多內容陸續奉上。

    PS:因公眾號平台更改了推播規則,如果不想錯過內容,記得讀完點一下在看,加個星標,這樣每次新文章推播才會第一時間出現在你的訂閱列表裏。

    「在看」支持小哈呀,謝謝啦