如何在六边形架构中使用Spring Cache?领域层能否用Spring Cache?
是否可以在领域层使用Spring Cache
不可以。
六边形架构的核心规则就是领域层作为系统内核,只承载纯业务逻辑,不能依赖任何外部框架、基础设施的具体实现。Spring Cache是Spring生态提供的技术组件,属于典型的非业务关注点,如果直接在领域层的实体、领域服务、值对象里引入@Cacheable这类注解或者Spring Cache相关API,会直接打破领域层的独立性:
- 领域逻辑和Spring框架强绑定,后续要替换缓存方案、甚至脱离Spring环境运行(比如写纯领域逻辑的单元测试)都会被框架卡住
- 缓存这种技术优化逻辑和核心业务逻辑混在一起,后续改缓存规则的时候很容易误改业务逻辑,维护成本会越来越高
简单说,缓存是技术细节,根本没资格进领域层。
六边形架构中正确使用Spring Cache的方式
缓存本质是输出适配器(基础设施侧的适配组件)要处理的事,所有和Spring Cache相关的代码、配置、注解都应该收敛在基础设施层,绝对不向上侵入领域层、应用层的核心逻辑,落地按这几步走就行:
- 先在领域层定义抽象端口
涉及数据查询、写入的需求,先在领域层定义无任何框架依赖的Java接口(也就是六边形架构里的输出端口),接口方法只描述业务语义,不带任何缓存相关的注解或者参数。举个最简单的例子:// 归属domain层,零框架依赖 public interface ProductRepository { Product getById(Long productId); void save(Product product); } - 在基础设施层实现端口,接入Spring Cache
写一个端口的实现类放在infrastructure层,这个类属于和技术细节打交道的适配器,可以自由使用Spring Cache的所有能力:不管是@Cacheable、@CacheEvict这类注解,还是直接注入CacheManager做自定义的缓存操作,都不会影响上层逻辑。示例代码:// 归属infrastructure层,允许依赖Spring框架 @Component public class ProductRepositoryImpl implements ProductRepository { @Autowired private ProductBaseMapper productBaseMapper; @Override @Cacheable(cacheNames = "product_detail", key = "#productId") public Product getById(Long productId) { return productBaseMapper.selectById(productId); } @Override @CacheEvict(cacheNames = "product_detail", key = "#product.id") public void save(Product product) { productBaseMapper.updateById(product); } } - 上层逻辑只依赖抽象端口,完全感知不到缓存存在
不管是领域服务还是应用服务,都只注入领域层定义的ProductRepository抽象接口调用方法,根本不知道底层是直接查了数据库、走了Spring Cache,还是后续换成了Caffeine本地缓存、Redis分布式缓存,核心业务逻辑一行都不用改。写单元测试的时候,直接Mock这个端口接口就行,不需要启动Spring容器,也不用处理缓存带来的测试数据污染问题。
补充一个常见的折中场景:如果是简单的业务项目,不想额外拆这么多层,临时在应用层的服务类上加Spring Cache注解也不算大问题,至少不会污染最核心的领域层,但长期维护的话,还是把缓存这类技术细节下沉到基础设施层的适配器里,架构的干净程度会高很多。
内容的提问来源于stack exchange,提问作者chupee120
相关产品推荐
相关产品推荐

