[英]Clean Architecture and Cache Invalidation
我有一個嘗試遵循 Clean Architecture 的應用程序,我需要做一些緩存失效,但我不知道這應該在哪一層完成。
為了這個例子,假設我有一個OrderInteractor
有 2 個用例: getOrderHistory()
和sendOrder(Order)
。
第一個用例是使用OrderHistoryRepository
,第二個用例是使用OrderSenderRepository
。 這些存儲庫是多個實現的接口(第一個是MockOrderHistoryRepository
和InternetOrderHistoryRepository
)。 OrderInteractor
僅通過接口與這些存儲庫交互,以隱藏真正的實現。
Mock
版本是非常虛擬的,但是歷史存儲庫的Internet
版本將一些數據保留在緩存中以更好地執行。
現在,我想實現以下內容:當訂單成功發送時,我想使歷史緩存失效,但我不知道我應該在哪里執行實際的緩存失效。
我的第一個猜測是向OrderHistoryRepository
添加一個invalidateCache()
並在交互器內的sendOrder()
方法的末尾使用此方法。 在InternetOrderHistoryRepository
,我只需要實現緩存失效就可以了。 但是我將被迫在MockOrderHistoryRepository
內部實際實現該方法,並且它向外部公開了一些緩存管理由存儲庫執行的事實。 我認為OrderInteractor
不應該意識到這種緩存管理,因為它是OrderHistoryRepository
的Internet
版本的實現細節。
我的第二個猜測是當InternetOrderSenderRepository
知道訂單已成功發送時執行緩存失效,但它會強制此存儲庫知道InternetOrderHistoryRepository
以便獲取此存儲庫用於緩存管理的緩存鍵。 而且我不希望我的OrderSenderRepository
依賴於OrderHistoryRepository
。
最后,我的第三個猜測是擁有某種CacheInvalidator
(無論名稱如何)接口,當存儲庫被CacheInvalidator
時使用Dummy
實現,當Interactor
器使用Internet
存儲庫時使用Real
實現。 這個CacheInvalidator
將被注入到Interactor
,並且選定的實現將由構建存儲庫和CacheInvalidator
的Factory
提供。 這意味着我將有一個MockedOrderHistoryRepositoryFactory
- 構建MockedOrderHistoryRepository
和DummyCacheInvalidator
- 和InternetOrderHistoryRepositoryFactory
- 構建InternetOrderHistoryRepository
和RealCacheInvalidator
。 但在這里,我不知道這個CacheInvalidator
是否應該由Interactor
在sendOrder()
結束時使用,或者直接由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.