DDD | 04-什么是聚合根
三、什么是聚合根?
聚合根(Aggregate Root)是DDD中的一個核心概念,用于組織和管理一組相關的領域對象,確保它們的整體一致性和完整性。聚合根是領域模型中的關鍵組件,它不僅封裝了領域內的復雜業務邏輯,還提供了控制訪問和維護數據一致性的機制,是構建可維護、可擴展的軟件系統的重要基石。
主要特點
- 聚合的概念:聚合是一組相關對象的集合,這些對象一起形成一個完整的業務概念,并作為一個單元進行操作。在聚合內部,對象之間有嚴格的關聯和依賴關系。
- 根實體:聚合根是聚合中的一個特殊實體,它是外部世界訪問聚合內部其他成員的唯一入口點。這意味著外部對象不能直接持有聚合內非根實體的引用,必須通過根實體來訪問和操作它們。
- 邊界定義:每個聚合都有一個清晰的邊界,這個邊界定義了聚合的范圍,以及哪些操作可以在聚合內部執行,哪些操作需要通過根實體來協調。這有助于維護數據的一致性和完整性。
- 一致性保證:聚合根負責維護其內部的所有業務規則和一致性約束。當外部嘗試修改聚合狀態時,所有必要的驗證和業務邏輯都在聚合根內部執行,確保操作的原子性和一致性。
- 唯一標識:聚合根擁有一個全局唯一的標識符(
ID),外部系統通過這個ID來識別和訪問聚合。這個ID也是與其他聚合建立關聯的基礎。 - 生命周期管理:聚合根還負責管理器內部對象的生命周期,包括創建、更新和刪除聚合內的實體和值對象。
設計原則
明確的邊界
- 聚合根定義了一個清晰的邊界,限定那些對象屬于聚合內部,那些屬于外部。邊界內的對象作為一個整體處理,對外部隱藏內部細節
單一入口點
- 聚合根是外界訪問聚合內部其他對象的唯一合法途徑。外部對象不能直接持有或操作聚合內的非根實體,所有交互都需通過根實體的方法進行
內聚性
- 聚合內的所有對象應緊密相關,共同完成一個業務功能。這意味著聚合內的對象和操作應該圍繞著一個核心業務概念組織
一致性規則封裝
- 聚合根負責維護其內部的一致性,包括業務規則、驗證邏輯等。所有改變聚合狀態的操作都應該在根實體中實現,確保操作的原子性和業務規則的遵守
標識唯一性
- 每個聚合根都有一個全局唯一的標識符(
ID), 用以區分不同的聚合實例。這個ID也用于外部對聚合的引用
生命周期管理
- 聚合根控制其內部成員(實體和值對象)的生命周期,包括它們的創建、更新和刪除
有限的大小
- 為了保持聚合的可管理和易于理解,通常建議聚合的大小不要過大。過大的聚合可能導致性能問題和復雜度增加
聚合內部引用
- 聚合內部的對象可以通常引用相互協作,但這些引用應限制在聚合邊界之內。對合聚合間的關聯,通常適用ID進行間接引用
事務邊界
- 在事務處理中,聚合根常常作為事務的邊界,確保事務內的所有操作要么全部成功,要么全部失敗,以此來維護數據的完整性
問題探討
聚合根和實體對象有什么區別?
身份標識(Identity)
- 實體(Entity):實體具有唯一標識(
ID),這個ID用來區分領域中的每一個單獨的實體實例。實體的ID在整個系統范圍內具有唯一性 - 聚合根 (Aggregate Root):聚合根也是一個實體,但它在一個聚合中扮演領導角色。它的ID不僅在系統范圍內是唯一的,而且是外部世界訪問聚合內部其他實體的入口點
聚合邊界(Aggregate Boundary)
- 實體:實體可以是聚合內部的一部分,也可以是獨立存在的。如果實體是聚合內部的一部分,則其ID在聚合內部唯一即可,外部訪問該實體必須通過聚合根
- 聚合根:定義了聚合的邊界,決定了那些對象屬于聚合內部。外部對象不能直接訪問聚合內的非根實體,只能通過聚合根暴露的接口進行操作
職責與控制
- 實體:實體主要負責維護自身的狀態和行為,可能包含一些簡單的業務邏輯
- 聚合根:負責維護整個聚合的一致性,包括內部實體的狀態更改和業務規則的執行。它控制著對聚合內部元素的所有修改,確保在任何時刻聚合都處于有效狀態
生命周期關聯
- 實體:實體的生命周期通常由其所在聚合根或外部服務(如
Repository)管理 - 聚合根:除了管理自己的生命周期外,還間接管理其內部實體的生命周期,決定它們的創建、更新和刪除
訪問控制
- 實體:如果不是聚合根,實體通常不直接暴露給外部,外部組件不應直接持有實體的引用
- 聚合根:是外部訪問聚合內部的唯一合法通道,提供對外接口,隱藏內部實現細節和復雜性
綜上所述,聚合根是一種特殊的實體,它不僅代表一個獨立的業務概念,還負責協調和保護其內部的其他實體和值對象,確保整體的業務規則得到執行,維持數據的一致性和完整性。實體則是領域模型中的基本構建塊,標識具有唯一標識的領域對象,而聚合根在實體之上提供了一層額外的結構和控制。
你覺得User是一個實體對象還是聚合根?
在大多數情況下,User 通常被設計為一個聚合根,因為它能更好地適應業務需求的變化和復雜性。
- 唯一標識符:
User擁有一個全局唯一的標識符(如用戶ID),這滿足實體的基本特征 - 業務操縱的中心:用戶賬戶是許多業務操作的中心,如登陸、資料編輯、權限管理等。這些操作往往涉及到用戶信息的修改和驗證,因此需要一個統一的入口點來維護這些操作的一致性和安全性,這正是聚合根的作用
- 關聯管理:用戶可能與其他領域對象關聯,比如用戶可能擁有多個地址、多個角色或者與多個訂單相關聯。作為聚合根,
User可以管理這些關聯關系,控制對這些關聯對象的訪問和修改,確保數據的完整性和一致性 - 權限和安全:在很多系統中,用戶數據是非常敏感的,需要嚴格控制訪問權限。通過
User設計為聚合根,可以更集中地實施安全策略和訪問控制邏輯
代碼示例
設計一個訂單聚合類(OrderAggregate)
/**
* 訂單聚合類,封裝了訂單的相關信息和行為。
* 包括訂單ID、顧客ID、訂單狀態、訂單項、支付狀態等。
*/
public class OrderAggregate {
private final UUID orderId;
private final UUID customerId;
private OrderStatusVO status;
private final List<OrderLineItemEntity> lineItems;
private boolean isPaid;
/**
* 構造函數初始化訂單聚合體。
* @param orderId 訂單ID
* @param customerId 顧客ID
* @param status 訂單初始狀態
*/
public OrderAggregate(UUID orderId, UUID customerId, OrderStatusVO status) {
this.orderId = orderId;
this.customerId = customerId;
this.status = status;
this.lineItems = new ArrayList<>();
this.isPaid = false;
}
/**
* 添加訂單項。
* 如果訂單已支付,則拋出IllegalStateException異常。
* @param lineItem 要添加的訂單項
*/
public void addLineItem(OrderLineItemEntity lineItem) {
if (!isPaid) {
lineItems.add(lineItem);
} else {
throw new IllegalStateException("Cannot add line item to paid order");
}
}
/**
* 移除訂單項。
* @param lineItem 要移除的訂單項
*/
public void removeLineItem(OrderLineItemEntity lineItem) {
lineItems.remove(lineItem);
}
/**
* 標記訂單為已支付。
* 如果訂單已支付,則拋出IllegalStateException異常。
*/
public void pay() {
if (!isPaid) {
isPaid = true;
status = OrderStatusVO.PAID;
} else {
throw new IllegalStateException("Order is already paid");
}
}
/**
* 更改訂單狀態。
* @param newStatus 新的訂單狀態
*/
public void changeStatus(OrderStatusVO newStatus) {
this.status = newStatus;
}
/**
* 計算訂單的總金額。
* @return 訂單的總金額
*/
public BigDecimal calculateTotalAmount() {
return lineItems.stream()
.map(OrderLineItemEntity::calculateTotalPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
/**
* 獲取訂單ID。
* @return 訂單ID
*/
public UUID getOrderId() {
return orderId;
}
/**
* 獲取顧客ID。
* @return 顧客ID
*/
public UUID getCustomerId() {
return customerId;
}
/**
* 獲取訂單狀態。
* @return 訂單狀態
*/
public OrderStatusVO getStatus() {
return status;
}
/**
* 獲取訂單項列表。
* @return 訂單項列表
*/
public List<OrderLineItemEntity> getLineItems() {
return lineItems;
}
/**
* 檢查訂單是否已支付。
* @return 如果訂單已支付返回true,否則返回false
*/
public boolean isPaid() {
return isPaid;
}
/**
* 返回訂單聚合體的字符串表示。
* @return 訂單聚合體的字符串表示
*/
@Override
public String toString() {
return "OrderAggregate{" +
"orderId=" + orderId +
", customerId=" + customerId +
", status=" + status +
", lineItems=" + lineItems +
", isPaid=" + isPaid +
'}';
}
/**
* 比較兩個訂單聚合體是否相等。
* @param o 要比較的訂單聚合體
* @return 如果兩個訂單聚合體相等返回true,否則返回false
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
OrderAggregate that = (OrderAggregate) o;
return isPaid == that.isPaid && Objects.equals(orderId, that.orderId) && Objects.equals(customerId, that.customerId) && status == that.status && Objects.equals(lineItems, that.lineItems);
}
/**
* 計算訂單聚合體的哈希碼。
* @return 訂單聚合體的哈希碼
*/
@Override
public int hashCode() {
return Objects.hash(orderId, customerId, status, lineItems, isPaid);
}
}

浙公網安備 33010602011771號