DDD | 05-什麼是倉儲層

Neking發表於2024-07-15

四、什麼是倉儲層?

在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等)互動的細節,確保領域層可以透過一個抽象的介面與這些底層技術解耦。這樣,領域層的程式碼就不需要關心資料是如何被儲存或檢索的,從而提高了程式碼的可維護性和靈活性。

相關文章