DDD 之聚合(Aggregate)

聚合(Aggregate)是領域驅動設計(Domain-Driven Design,簡稱 DDD)中的一個重要概念,用於將一組關聯的領域對象組織在一起。

  1. 聚合的定義

聚合是指一組相關聯的領域對象的根實體,由根實體和其內部的實體和值對象組成,並定義了這些對象在業務中的邊界和一致性約束。聚合的根實體是與外部世界交互的入口點,它負責保持聚合內部的一致性,並封裝了其他對象的訪問。

  1. 聚合的設計原則

在設計聚合時,需要遵循一些原則,以確保聚合的一致性和可變性。

2.1 單一職責原則(SRP)

每個聚合應該有一個明確的責任和核心業務功能,並且聚合內部的各個對象應該負責不同的任務,實現單一職責。這樣做可以提高聚合的可維護性和可測試性。

2.2 邊界原則

通過定義聚合的邊界,我們可以將聚合劃分爲內部對象和外部對象。外部對象只能通過聚合根進行訪問,內部對象不能直接被外部調用和修改。這有助於保持聚合內部的一致性,並減少不必要的耦合。

2.3 一致性邊界原則(Aggregate Consistency Boundary)

聚合應該形成一個一致性邊界,內部對象之間的操作和狀態變更應該在聚合根中進行協調和控制。聚合根負責保持聚合的一致性,任何對聚合內部對象的修改都應該通過聚合根進行,以保證數據的完整性。

2.4 事務邊界原則

聚合應該成爲一個事務邊界,內部的所有對象的操作都應該在同一個事務中執行。這樣可以確保聚合的一致性,並避免數據的髒讀或不一致狀態。

  1. 聚合的實際應用

在實際應用中,聚合用於封裝複雜的業務邏輯和對象之間的關係。以下是一些在使用領域驅動設計時常見的聚合使用情景:

3.1 聚合根的操作

聚合的根實體負責定義和執行與聚合相關的操作,並控制內部對象的操作。聚合根可以處理領域事件、觸發領域行爲和修改聚合內部對象的屬性。

3.2 領域事件處理

聚合可以通過發佈領域事件來通知其他聚合和領域服務發生了某個重要的業務行爲。其他聚合可以通過訂閱領域事件來進行相關的處理,保持相應的一致性。

3.3 聚合之間的關係

聚合之間可以存在關聯和依賴的關係。在領域驅動設計中,聚合之間的關係通常通過聚合根之間的引用來進行管理。關聯關係可以是一對一、一對多或多對多等不同的關係。

3.4 值對象的封裝

聚合可以封裝值對象,將多個值對象作爲一個整體進行處理。對於某些業務上相關聯、不可分割的屬性,可以將其封裝成值對象,並作爲聚合的一部分進行處理。

  1. 代碼演示

假設我們正在開發一個博客系統,其中包含文章和評論。這邊結合聚合根、領域事件處理、聚合之間的關係和值對象的封裝。

首先,定義一個聚合根類(Aggregate Root)Article,作爲文章的聚合根。

public class Article {
    private String id;
    private String title;
    private String content;
    private List<Comment> comments;

    // 領域事件,用來發布評論添加事件
    private List<CommentAddedEvent> commentAddedEvents;
    
    public Article(String title, String content) {
        this.id = UUID.randomUUID().toString();
        this.title = title;
        this.content = content;
        this.comments = new ArrayList<>();
        this.commentAddedEvents = new ArrayList<>();
    }
    
    // 省略其他構造函數和getter/setter方法
    
    public void addComment(String commentContent, String commenter) {
        Comment newComment = new Comment(commentContent, commenter);
        comments.add(newComment);
        
        // 發佈評論添加事件
        CommentAddedEvent event = new CommentAddedEvent(id, newComment);
        commentAddedEvents.add(event);
    }
    
    public void deleteComment(Comment comment) {
        comments.remove(comment);
    }
    
    // 獲取歷史添加評論事件
    public List<CommentAddedEvent> getCommentAddedEvents() {
        return Collections.unmodifiableList(commentAddedEvents);
    }
}

接下來,定義一個評論類(Comment)作爲聚合的子實體。評論還可以被拆分爲更小的值對象,例如評論內容(CommentContent)和評論者(Commenter)。

public class Comment {
    private CommentContent content;
    private Commenter commenter;
    private Date createdAt;
    
    public Comment(String commentContent, String commenterName) {
        this.content = new CommentContent(commentContent);
        this.commenter = new Commenter(commenterName);
        this.createdAt = new Date();
    }
    
    // 省略getter方法
}

public class CommentContent {
    private String value;
    
    public CommentContent(String value) {
        // 對傳入的值進行驗證,確保評論內容的有效性
        this.value = value;
    }
    
    // 省略getter方法
}

public class Commenter {
    private String name;
    
    public Commenter(String name) {
        // 對傳入的值進行驗證,確保評論者的有效性
        this.name = name;
    }
    
    // 省略getter方法
}

在我們的博客系統中,我們可以使用聚合來進行文章的創建和評論的添加。同時,我們可以監聽評論添加事件,進行一些額外的處理。

public class BlogSystem {
    public static void main(String[] args) {
        Article article = new Article("文章標題""文章具體的內容......");
        
        // 添加評論
        article.addComment("寫的非常好""老李");
        article.addComment("從小虎哥的技術博客裏面受益匪淺""老王");
        
        // 監聽評論添加事件,並進行一些額外的處理
        List<CommentAddedEvent> commentAddedEvents = article.getCommentAddedEvents();
        for (CommentAddedEvent event : commentAddedEvents) {
            handleCommentAddedEvent(event);
        }
    }
    
    private static void handleCommentAddedEvent(CommentAddedEvent event) {
        // 處理評論添加事件,例如發送通知郵件
        System.out.println("有新的評論 " + event.getArticleId() + ": " + event.getComment().getContent().getValue());
    }
}

在這個示例中,我們引入了領域事件的概念,當評論被添加到文章中時,會發佈一個CommentAddedEvent事件。我們可以在BlogSystem類中監聽該事件,並進行一些額外的處理,比如發送通知郵件。

  1. 總結

聚合是領域驅動設計中的重要概念,用於表示一組相關聯的領域對象。在聚合的設計中,需要遵循單一職責原則、邊界原則、一致性邊界原則和事務邊界原則,以確保聚合的一致性和可變性。在實際應用中,聚合用於封裝複雜的業務邏輯和對象之間的關係,並提供了一致性邊界和事務邊界。聚合往往是領域驅動設計中的核心對象,對於領域模型的設計和實現具有重要的影響。


本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/QSh_OyeKU2rfgL9s0tUQ7A