清洁体系结构和缓存失效

pde*_*d59 5 java android clean-architecture

我有一个试图遵循清洁架构的应用程序,我需要做一些缓存失效,但我不知道应该在哪一层做这个.

为了这个例子,假设我有OrderInteractor2个用例:getOrderHistory()sendOrder(Order).

第一个用例是使用a OrderHistoryRepository,第二个用例是使用a OrderSenderRepository.这些存储库是具有多个实现(MockOrderHistoryRepository以及InternetOrderHistoryRepository第一个)的接口.该OrderInteractor只与经过界面论文库为了隐藏真正实现交互.

Mock版本非常虚拟,但Internet历史存储库的版本将一些数据保留在缓存中以更好地执行.

现在,我想实现以下内容:当成功发送订单时,我想使历史记录的缓存无效,但我不知道应该在哪里执行实际的缓存失效.

我的第一个猜测是一个补充invalidateCache()OrderHistoryRepository,并在结束使用此方法sendOrder()交互器内的方法.在InternetOrderHistoryRepository,我将只需要实现缓存失效,我会很好.但我将被迫在其中实际实现该方法,MockOrderHistoryRepository并且向外部暴露了存储库执行某些缓存管理的事实.我认为OrderInteractor不应该知道这个缓存管理,因为它是Internet版本的实现细节OrderHistoryRepository.

我的第二个猜测是InternetOrderSenderRepository当它知道订单已成功发送时执行缓存失效,但它会强制此存储库知道InternetOrderHistoryRepository,以便获取此repo用于缓存管理的缓存密钥.而且我不希望我OrderSenderRepository依赖于OrderHistoryRepository.

最后,我的第三个猜测是使用某种CacheInvalidator(无论名称)接口与Dummy存储库被模拟RealInteractor使用的实现以及使用Internet存储库时的实现.这CacheInvalidator将注入到,Interactor并且选定的实现将由Factory构建存储库和存储库提供CacheInvalidator.这意味着我将拥有MockedOrderHistoryRepositoryFactory- 正在构建MockedOrderHistoryRepositoryDummyCacheInvalidator- 以及InternetOrderHistoryRepositoryFactory- 正在构建InternetOrderHistoryRepository和构建RealCacheInvalidator.但是在这里,我不知道这CacheInvalidator应该是由Interactor最后使用sendOrder()还是直接使用InternetOrderSenderRepository(尽管我认为后者更好,因为交互者可能不应该知道在引擎盖下有一些缓存管理).

您最喜欢的构建方式是什么?

非常感谢你.皮埃尔

Ren*_*ink 2

您的第二个猜测是正确的,因为缓存是持久性机制的一个细节。例如,如果存储库是基于文件的存储库,则缓存可能不是问题(例如本地 SSD)。

交互者(用例)根本不应该了解缓存。这将使测试变得更容易,因为您不需要真正的缓存或模拟来进行测试。

我的第二个猜测是,当它知道订单已成功发送时,会在内部执行缓存失效,InternetOrderSenderRepository但它将强制此存储库知道,InternetOrderHistoryRepository以便获取此存储库用于缓存管理的缓存密钥。

It seems that your cache key is a composite of multiple order properties and therefore you need to encapsulate the cache key creation logic somewhere for reuse.

In this case, you have the following options:

One implementation for both interfaces

You can create a class that implements the InternetOrderSenderRepository as well as the InternetOrderHistoryRepository interface. In this case, you can extract the cache key generation logic into a private method and reuse it.

Use a utility class for the cache key creation

Simple extract the cache key creation logic in a utility class and use it in both repositories.

Create a cache key class

A cache key is just an arbitrary object because a cache must only check if a key exists and this means use the equals method that every object has. But to be more type-safe most caches use a generic type for the key so that you can define one.

Thus you can put the cache key logic and validation in an own class. This has the advantage that you can easily test that logic.

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

您可以使用此类作为缓存的键类型: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);
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以在两个存储库实现中使用OrderCache,还可以将接口隔离与上面的缓存键类结合起来。

如何申请

  • 您可以使用面向方面的编程和上述选项之一来实现缓存
  • 您可以为每个存储库创建一个包装器(或委托),以便在需要时将缓存和委托应用到实际存储库。这与面向方面的方式非常相似。您只需手动实现该方面即可。