通过新增接口解决循环依赖及单一职责原则(SRP)相关疑问
你对仓储职责的直觉理解是从「操作对象」维度出发的,但SRP的核心判断标准是一个类应该只有一个被修改的原因,和操作对象是否为同一个实体没有必然关系,我们可以从两个维度拆解这个问题:
1. 原SqlUserRepository的双重职责冲突
原来的SqlUserRepository实际上承载了两个完全独立、使用场景完全割裂的职责:
- 职责A:面向业务逻辑层提供用户数据的读写能力,其中写操作(
Update)需要触发审计日志写入,因此必须依赖IAuditTrailAppender,这部分逻辑的变更来源是业务规则迭代 - 职责B:面向身份上下文层提供「按用户名查询用户」的能力,供
AspNetUserContextAdapter获取当前登录用户,这部分查询是审计日志初始化的前置依赖——审计日志要记录操作人ID,必须先拿到当前用户信息,因此这部分逻辑完全不需要、也不能依赖审计组件,它的变更来源是身份认证规则的调整
两个职责的变更原因完全独立,同时放在SqlUserRepository里才是违反SRP的核心原因,而不是GetByName本身不该存在。
2. 作者的方案不是变通,是职责拆分的标准解法
你认为的「变通」是默认把「所有用户相关的数据库操作」都应该归到同一个仓储类里,但这个认知是对仓储模式的误解:仓储的核心作用是封装领域层对数据层的访问,而不是把同一个实体的所有CRUD强行塞到同一个类里。
作者拆分出IUserByNameRetriever的本质,是把「供身份认证使用的只读查询」和「供业务逻辑使用的带审计的读写操作」拆分到两个独立的类里,两个类各自只有一个变更原因:
SqlUserRepository只响应业务规则的变更,不需要再为身份认证的调整修改代码SqlUserByNameRetriever只响应身份认证规则的变更,和审计逻辑完全解耦
拆分后自然就切断了原来的循环依赖链,同时完全符合SRP的要求。
内容的提问来源于stack exchange,提问作者user16276760
相关产品推荐
相关产品推荐

