Repository + Service 层常见循环依赖问题
Repository + Service 层常见循环依赖问题
一、典型场景:Repository ↔ Service 互相调用
先给你一个你一定见过(或未来一定会遇到)的例子:
public class UserService
{
private readonly IUserRepository _userRepository;
private readonly IOrderService _orderService;
public UserService(IUserRepository userRepository, IOrderService orderService)
{
_userRepository = userRepository;
_orderService = orderService;
}
}
public class OrderService
{
private readonly IOrderRepository _orderRepository;
private readonly IUserService _userService;
public OrderService(IOrderRepository orderRepository, IUserService userService)
{
_orderRepository = orderRepository;
_userService = userService;
}
}典型循环依赖链:
- UserService 要 OrderService
- OrderService 要 UserService
DI 容器直接炸了:
“你要他,他要你,你俩自己玩吧。”
武侠理解
- 用户掌门(UserService)练功需要订单掌门(OrderService)
- 订单掌门练功需要用户掌门
两个掌门互相“依赖内功”,没有谁能先修炼 → 死循环。
二、为什么 Repository ↔ Service 很容易循环依赖?
因为很多新人误把 Repository 当成 “迷你 Service”。
例如:
public interface IUserRepository
{
User GetUserDetail(long id);
}然后在 Repository 中调用 Service:
public class UserRepository : IUserRepository
{
private readonly IUserService _userService;
}⚠ Repository 层应该很纯粹,只负责数据访问,不应该依赖 Service!!!
只要 Repository 调 Service,Service 再调 Repository,很容易出现:
Repository → Service → Repository → Service → …
三、为什么会写出循环依赖?
① 职责分离不清晰
Service 本应负责业务,Repository 负责数据访问,但开发者把“计算逻辑”放到了 Repository 中。
② Service 层互相调用太多
例如:
- 用户服务要查订单
- 订单服务要查用户
- 商品服务也要查用户
久而久之互相形成网状依赖。
③ Repository 被当工具类使用
错误示例:
_userRepository.CheckUserPermission(...)Repository 永远不应该做权限校验、业务判断!
四、如何解决
方法一:分离查询与业务
把查询从 Service 拆出去:
新建 QueryService(或 DomainQuery)专门用于查询:
UserService → 处理业务逻辑
UserQuery → 处理查询
OrderService → 处理业务逻辑
OrderQuery → 处理查询所有 Service 需要用户信息时 → 调 Query 而不是调 Service
示例:
public class UserQuery : IUserQuery
{
private readonly IUserRepository _userRepository;
}这样 OrderService 只依赖 UserQuery,不依赖 UserService,就不会形成循环。
方法二:事件通知(避免主动调用)
例如:
- 订单创建后需要更新用户积分
- 不要让 OrderService 调 UserService
- 而是让 OrderService 发布事件
public class OrderCreatedEvent : INotification
{
public long UserId { get; set; }
public decimal Amount { get; set; }
}UserService 监听:
public class OrderEventHandler : INotificationHandler<OrderCreatedEvent>
{
public Task Handle(OrderCreatedEvent e)
{
// 更新积分
}
}➡ 解耦 + 可扩展 + 无循环依赖
方法三:Factory(工厂注入,小范围解耦)
例如只某些业务流程中需要临时创建 service:
public class OrderService
{
private readonly Func<IUserService> _userServiceFactory;
}取到的时候才创建,不会循环。
方法四:Domain Service + Application Service
这是 DDD 的标准做法:
Application Service
- 负责 orchestrate(流程编排)
- 调用多个 Repository
- 不做领域逻辑
Domain Service
- 专注某个纯业务逻辑
- 永远不依赖 ApplicationService
这样:
ApplicationService → DomainService → Repository没有任何循环可能。
五、企业级典型重构示例
❌ 错误示例(循环依赖):
UserService → OrderService
OrderService → UserService✔ 正确改法:
UserService → UserRepository
OrderService → OrderRepository
OrderService → UserQuery (只读查询,不依赖业务)
UserQuery → UserRepositoryService 不再互相调用,循环链被斩断。
六、总结(循环依赖的判断口诀)
不要让:
- Repository 调 Service
- Service A 调 Service B,再反过来 B 调 A
- Service 做查询
- Repository 做业务
要做到:
- Service 只做业务逻辑
- Query 专门负责查询
- Repository 只做数据访问
- 事件而不是主动调用