清洁架构中缓存位置选择:微服务缓存实现方案咨询
缓存逻辑的合理分层实现方案
两种方案的优劣与适用场景
方案1:服务层处理缓存缺失(不推荐)
这种方式会让服务层承担原本属于数据访问层的职责,破坏单一职责原则。服务层应聚焦业务逻辑,而非数据缓存、读取的细节。后续若要移除缓存,必须修改服务层代码,违反开闭原则,还会让服务层与具体仓储实现耦合,失去接口解耦的意义。
方案2:仓储层处理缓存缺失(可优化)
这个思路的核心是正确的——仓储层的职责就是封装数据获取细节,不管数据来自缓存还是数据库,服务层只需调用CustomerRepository接口即可。你担心的循环依赖问题,可通过装饰器模式解决:
- 让
CacheCustomerRepositoryImpl依赖CustomerRepository接口(而非具体的DbCustomerRepositoryImpl) - 缓存缺失时,调用注入的
CustomerRepository实例(即DbCustomerRepositoryImpl)获取数据,写入缓存后返回 - 这种方式下,
CacheCustomerRepositoryImpl是对底层仓储的增强,而非独立实现,完全避免循环依赖
示例代码结构:
public class CacheCustomerRepositoryImpl implements CustomerRepository { private final CustomerRepository delegate; private final Cache cache; // 构造注入依赖的仓储实现 public CacheCustomerRepositoryImpl(CustomerRepository delegate, Cache cache) { this.delegate = delegate; this.cache = cache; } @Override public Customer findById(int id) { Customer customer = cache.get(id); if (customer == null) { customer = delegate.findById(id); if (customer != null) { cache.put(id, customer); } } return customer; } }
缓存业务逻辑返回结果的处理方式
如果需要缓存的是业务逻辑的计算结果(而非单纯仓储数据),缓存逻辑就不属于仓储层了,推荐两种方式:
- AOP切面实现:在服务层方法上添加自定义缓存注解,通过切面统一处理缓存的读取、写入和失效。这种方式完全不侵入业务代码,符合清洁架构“业务逻辑与基础设施解耦”的原则。
- 独立缓存服务:创建专门的
CustomerCacheService封装业务结果的缓存逻辑,服务层按需调用该缓存服务,再根据缓存结果决定是否执行业务逻辑。注意避免缓存服务与业务服务产生耦合。
清洁架构下的最优实践
清洁架构中,缓存属于基础设施层(最外层),需遵循以下原则:
- 业务层(服务层)完全不感知缓存存在,仅依赖抽象的仓储接口
- 缓存逻辑通过装饰器或切面封装在基础设施层,作为对仓储或服务的增强
- 所有依赖方向为内层(业务层)依赖外层(基础设施层),避免反向依赖
这样既保证各层职责清晰,又能灵活添加、移除缓存逻辑,无需修改核心业务代码。
内容的提问来源于stack exchange,提问作者potatoxchip
相关产品推荐
相关产品推荐

