四、什麼是倉儲層?
在DDD中,倉儲(Repository)是一種設計模式,它充當了領域層與資料儲存層之間的橋樑。倉儲的主要職責是提供一種抽象機制,使得領域物件(尤其是聚合根)可以被透明地持久化和檢索,而無需暴露底層的資料訪問技術細節給領域層。這樣設計的目的是為了保持領域模型的純淨性,讓業務邏輯不受資料訪問技術的影響,同時也促進了程式碼的可測試性和可維護性。
主要特點
介面定義在領域層
- 倉儲介面定義了諸如儲存(save)、查詢(find)、更新(update)和刪除(delete)領域物件的方法,這些介面屬於領域層從一部分,確保了領域邏輯的獨立性
實現位於基礎設施層
- 雖然倉儲介面屬於領域層,但它的具體實現通常位於基礎設施層(Infrastructure Layer)。實現細節回依賴於具體的持久化技術,如關聯式資料庫、NoSQL資料庫、記憶體資料庫等
隔離領域層和資料訪問層
- 透過倉儲模式,領域層的物件可以不知道也不關心資料是如何儲存和檢索的。這有助於降低各層之間的耦合度,使得領域邏輯更加集中和純粹
提供一致的領域物件檢視
- 倉儲還負責管理領域物件的生命期,確保從儲存中檢索出來的物件符合領域模型的規則,例如,確保聚合的一致性
支援領域驅動設計的原則
- 如
Ubiquitous Language(通用語言)
、業務優先和關注點分離等原則,透過倉儲的抽象,可以更好地在程式碼中體現業務領域的概念和規則
問題探討
倉儲層和聚合根的區別是什麼?
淺顯解釋
聚合根
- 想象你有一個裝滿東西的盒子,這個盒子就是聚合根。盒子裡的東西(比如小玩具、文具等)代表聚合內的其他實體和值物件。盒子外面有個標籤,寫著裡面有什麼,這就是聚合根的標識。當你相拿走或放回盒子裡的東西是,必須透過盒子(聚合根)來做,不能直接對盒子裡的物品動手腳。聚合根確保盒子裡的東西按規則擺放,保持整齊(即業務規則的一致性)
倉儲層
- 想象有一個大倉庫,裡面存放著很多這樣的盒子(聚合根)。倉庫管理員(倉儲層)知道怎麼找到每一個盒子,並且能幫你把盒子放進倉庫貨從倉庫裡拿出來。你告訴管理員需要哪個盒子,管理員就會處理好一切,你不需要知道倉庫是怎麼排列這些盒子的,也不用管它是用叉車還是傳送帶搬運的(即資料訪問的具體實現)
因此,聚合根是關於管理業務物件內部的規則和結構,而倉儲層是關於如何儲存和獲取這些業務物件到資料儲存中。聚合根關注"做什麼",倉儲層關注"怎麼存取"。
術語解釋
聚合根
- 概念:聚合根是聚合中的一個特殊實體,它作為聚合的入口點,負責維護聚合內部的一致性。聚合是一組相關物件的集合,這些物件一起被當作一個單元來對待,對外界隱藏起內部結構和複雜性
- 職責:聚合根確保所有的業務規則在聚合內部得到遵守,它控制著對聚合內其他實體和值物件的訪問。外部物件不能直接訪問聚合內的非根實體,只能透過聚合根進行操作
- 示例:在一個電子商務系統中,訂單
Order
可能是一個聚合根,它管理者訂單項OrderItem
、客戶資訊Customer
等內部實體,確保例如訂單總額的計算規則得到正確執行
倉儲層
- 概念:倉儲是一個設計模式,它提供了一種抽象機制,用於透明地持久化和檢索領域物件,尤其是聚合根。它就像是一個集合或資料庫的模擬,允許領域層以物件導向的方式運算元據,而無需關心資料儲存的具體細節
- 職責:倉儲的主要職責包括儲存
Save
、查詢Find
、更新Update
和刪除Delete
聚合根。它隔離了領域層和資料訪問層,使得領域邏輯可以獨立於具體的資料庫技術 - 示例:在一個電子商務系統中,
OrderRepository
可能提供方法來查詢特定ID的訂單、儲存新訂單到資料庫、更新訂單狀態等,而不需要訂單類知道這些操作是如何與資料庫互動的
區別總結
- 目的不同:聚合根關注於業務邏輯的封裝和領域內的不變性維護,而倉儲關注於領域物件的持久化和檢索機制
- 層次位置:聚合根是領域層的一部分,直接參與業務邏輯的實現;倉儲雖然定義在領域層,但起具體實現通常位於基礎設施層,作為領域層與資料儲存之間的適配層
- 互動方式:外部元件透過倉儲來操作聚合根,而不會直接操作聚合內部的其他實體。聚合根則負責維護其內部狀態和業務規則的一致性
簡而言之,聚合根是領域模型中的核心構造,負責業務邏輯的實現和一致性保護;而倉儲是領域層與資料儲存之間的中介,確保領域物件能夠被持久化和檢索,同時保持領域模型的純粹性
程式碼示例
寫一個訂單倉庫 (OrderRepository)
public interface OrderRepository {
/**
* 儲存訂單聚合根。
* @param order 待儲存的訂單聚合根物件,包含訂單的所有相關資訊。
* @return 儲存後的訂單聚合根物件。
*/
OrderAggregate save(OrderAggregate order);
/**
* 根據訂單ID查詢訂單聚合根。
* @param orderId 待查詢訂單的唯一標識。
* @return 如果找到,則返回包含訂單資訊的Optional物件;否則返回空Optional。
*/
Optional<OrderAggregate> findById(UUID orderId);
/**
* 刪除訂單聚合根。
* @param order 待刪除的訂單聚合根物件。
*/
void delete(OrderAggregate order);
/**
* 更新訂單狀態。
* @param orderId 待更新訂單的狀態的唯一標識。
* @param newStatus 新的訂單狀態資訊。
*/
void updateOrderStatus(UUID orderId, OrderStatusVO newStatus);
}
寫一個基於記憶體的訂單倉庫實現類OrderRepositoryImpl
public class OrderRepositoryImpl implements OrderRepository {
/**
* 使用HashMap儲存訂單聚合體,以訂單ID作為鍵,訂單聚合體作為值。
*/
private Map<UUID, OrderAggregate> orders = new HashMap<>();
/**
* 儲存訂單聚合體。
*
* @param order 要儲存的訂單聚合體。
* @return 返回儲存的訂單聚合體。
*/
@Override
public OrderAggregate save(OrderAggregate order) {
orders.put(order.getOrderId(), order);
return order;
}
/**
* 根據訂單ID查詢訂單聚合體。
*
* @param orderId 要查詢的訂單ID。
* @return 返回找到的訂單聚合體的Optional,如果找不到則為empty。
*/
@Override
public Optional<OrderAggregate> findById(UUID orderId) {
return Optional.ofNullable(orders.get(orderId));
}
/**
* 刪除訂單聚合體。
*
* @param order 要刪除的訂單聚合體。
*/
@Override
public void delete(OrderAggregate order) {
orders.remove(order.getOrderId());
}
/**
* 更新訂單狀態。
*
* @param orderId 要更新狀態的訂單ID。
* @param newStatus 新的訂單狀態。
*/
@Override
public void updateOrderStatus(UUID orderId, OrderStatusVO newStatus) {
Optional<OrderAggregate> optionalOrder = findById(orderId);
optionalOrder.ifPresent(order -> {
order.changeStatus(newStatus);
});
}
}
OrderRepositoryImpl
在DDD中所處位置?
屬於基礎設施層(Infrastructure Layer)的一部分。基礎設施層提供技術性服務,如資料庫訪問、訊息傳遞等,以支援領域層和應用層的功能。
具體分析,OrderRepository
是一個介面,它定義瞭如何存取領域物件OrderAggregate
到持久化儲存(如資料庫)中。這個介面屬於領域層或核心域(Core Domain),因為它表達了領域邏輯的一部分需求,即如何管理訂單聚合根的儲存和檢索。
而OrderRepositoryImpl
作為介面OrderRepository
的一個實現,通常駐留在基礎設施層。它實現了與具體資料儲存技術(如JDBC、Hibernate、Spring Data JPA等)互動的細節,確保領域層可以透過一個抽象的介面與這些底層技術解耦。這樣,領域層的程式碼就不需要關心資料是如何被儲存或檢索的,從而提高了程式碼的可維護性和靈活性。