簡體   English   中英

清潔架構和緩存失效

[英]Clean Architecture and Cache Invalidation

我有一個嘗試遵循 Clean Architecture 的應用程序,我需要做一些緩存失效,但我不知道這應該在哪一層完成。

為了這個例子,假設我有一個OrderInteractor有 2 個用例: getOrderHistory()sendOrder(Order)

第一個用例是使用OrderHistoryRepository ,第二個用例是使用OrderSenderRepository 這些存儲庫是多個實現的接口(第一個是MockOrderHistoryRepositoryInternetOrderHistoryRepository )。 OrderInteractor僅通過接口與這些存儲庫交互,以隱藏真正的實現。

Mock版本是非常虛擬的,但是歷史存儲庫的Internet版本將一些數據保留在緩存中以更好地執行。

現在,我想實現以下內容:當訂單成功發送時,我想使歷史緩存失效,但我不知道我應該在哪里執行實際的緩存失效。

我的第一個猜測是向OrderHistoryRepository添加一個invalidateCache()並在交互器內的sendOrder()方法的末尾使用此方法。 InternetOrderHistoryRepository ,我只需要實現緩存失效就可以了。 但是我將被迫在MockOrderHistoryRepository內部實際實現該方法,並且它向外部公開了一些緩存管理由存儲庫執行的事實。 我認為OrderInteractor不應該意識到這種緩存管理,因為它是OrderHistoryRepositoryInternet版本的實現細節。

我的第二個猜測是當InternetOrderSenderRepository知道訂單已成功發送時執行緩存失效,但它會強制此存儲庫知道InternetOrderHistoryRepository以便獲取此存儲庫用於緩存管理的緩存鍵。 而且我不希望我的OrderSenderRepository依賴於OrderHistoryRepository

最后,我的第三個猜測是擁有某種CacheInvalidator (無論名稱如何)接口,當存儲庫被CacheInvalidator時使用Dummy實現,當Interactor器使用Internet存儲庫時使用Real實現。 這個CacheInvalidator將被注入到Interactor ,並且選定的實現將由構建存儲庫和CacheInvalidatorFactory提供。 這意味着我將有一個MockedOrderHistoryRepositoryFactory - 構建MockedOrderHistoryRepositoryDummyCacheInvalidator - 和InternetOrderHistoryRepositoryFactory - 構建InternetOrderHistoryRepositoryRealCacheInvalidator 但在這里,我不知道這個CacheInvalidator是否應該由InteractorsendOrder()結束時使用,或者直接由InternetOrderSenderRepository (盡管我認為后者更好,因為交互器可能不知道有引擎蓋下的一些緩存管理)。

您首選的架構方式是什么?

非常感謝。 皮埃爾

您的第二個猜測是正確的,因為緩存是持久性機制的一個細節。 例如,如果存儲庫是基於文件的存儲庫緩存可能不是問題(例如本地 ssd)。

交互者(用例)根本不應該知道緩存。 這將使測試更容易,因為您不需要真正的緩存或模擬來進行測試。

我的第二個猜測是當InternetOrderSenderRepository知道訂單已成功發送時執行緩存失效,但它會強制此存儲庫知道InternetOrderHistoryRepository以便獲取此存儲庫用於緩存管理的緩存鍵

您的緩存鍵似乎是多個訂單屬性的組合,因此您需要將緩存鍵創建邏輯封裝在某處以供重用。

在這種情況下,您有以下選擇:

兩種接口的一種實現

您可以創建一個實現InternetOrderSenderRepository以及InternetOrderHistoryRepository接口的類。 在這種情況下,您可以將緩存密鑰生成邏輯提取到私有方法中並重復使用。

使用實用程序類來創建緩存鍵

簡單地提取實用程序類中的緩存鍵創建邏輯,並在兩個存儲庫中使用它。

創建緩存鍵類

緩存鍵只是一個任意對象,因為緩存必須只檢查鍵是否存在,這意味着使用每個對象都有的equals方法。 但是為了更加類型安全,大多數緩存使用通用類型作為鍵,以便您可以定義一個。

因此,您可以將緩存鍵邏輯和驗證放在自己的類中。 這樣做的好處是您可以輕松測試該邏輯。

public class OrderCacheKey {

    private Integer orderId;
    private int version;

    public OrderCacheKey(Integer orderId, int version) {
        this.orderId = Objects.requireNonNull(orderId);
        if (version < 0) {
            throw new IllegalArgumentException("version must be a positive integer");
        }
        this.version = version;
    }

    public OrderCacheKey(Order order) {
        this(order.getId(), order.getVersion());
    }

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        OrderCacheKey other = (OrderCacheKey) obj;

        if (!Objects.equals(orderId, other.orderId))
            return false;

        return Objects.equals(version, other.version);
    }

    public int hashCode() {
        int result = 1;
        result = 31 * result + Objects.hashCode(orderId);
        result = 31 * result + Objects.hashCode(version);
        return result;
    }

}

您可以使用此類作為緩存的鍵類型: Cache<OrderCacheKey, Order> 然后您可以在兩個存儲庫實現中使用OrderCacheKey類。

引入訂單緩存接口隱藏緩存細節

您可以應用接口隔離原則,將完整的緩存細節隱藏在一個簡單的接口后面。 這將使您的單元測試更容易,因為您必須減少模擬。

public interface OrderCache {

    public void add(Order order);
    
    public Order get(Integer orderId, int version);

    public void remove(Order order);
    
    public void removeByKey(Integer orderId, int version);
}

然后,您可以在兩個存儲庫實現中使用OrderCache ,也可以將接口隔離與上面的緩存鍵類結合使用。

如何申請

  • 您可以使用面向方面的編程和上述選項之一來實現緩存
  • 您可以為每個存儲庫創建一個包裝器(或委托),以便在需要時將緩存和委托應用於實際存儲庫。 這與面向方面的方式非常相似。 您只需手動實現方面。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM