仓储层混入领域逻辑如何规避?分层架构下用户查询方案抉择
方案选择与优化思路
一、现有方案对比及推荐
先明确分层架构的核心职责边界:
- Service层:负责封装业务逻辑,定义“什么是未活跃用户”这类业务规则
- Repository层:仅处理数据持久层操作,只关心如何从数据库取数据,不理解业务含义
方案1的问题
通用查询方法看似让仓储层“纯净”,但自定义的参数结构({ user: Partial<User>; dateTreshold: Date })会增加维护成本:后续如果有更多复杂查询条件,这个方法的参数会越来越臃肿,还容易出现参数格式不统一的问题,反而降低了扩展性。
方案2的问题
把“未活跃用户”的判定规则(isArchived=true、超过一个月未登录)硬编码在仓储层,违反了单一职责原则。仓储层不该知道业务规则——如果后续业务调整(比如改成两个月未活跃才算),你得改动仓储层代码,而仓储层本应只和数据库交互,和业务逻辑无关。
推荐优化方案
结合两者的优点,让仓储层提供基于数据库原生查询条件的通用查询方法,业务规则完全由Service层组装:
UserRepository.ts
@Injectable() export class UserRepository { constructor(private readonly prisma: PrismaService) { } async findAll(where: Prisma.UserWhereInput): Promise<User[]> { const users = await this.prisma.user.findMany({ where }); return users.map(user => new User(user)); } }
UserService.ts
@Injectable() export class UserService { constructor(private readonly userRepository: UserRepository) {} async getInactiveUsers(): Promise<User[]> { // 计算一个月前的日期 const oneMonthAgo = new Date(); oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1); // 组装业务规则对应的查询条件 const queryConditions: Prisma.UserWhereInput = { isArchived: true, lastVisitedDate: { lt: oneMonthAgo } }; return this.userRepository.findAll(queryConditions); } }
这个方案的优势:
- 仓储层只做数据查询,不涉及任何业务逻辑,职责清晰
- Service层完全掌控业务规则,后续调整只需要修改Service,不会影响数据访问层
- 复用Prisma原生的
UserWhereInput,不需要自定义复杂的参数结构,降低维护成本
二、其他实现思路
1. 抽离查询对象(Query Object)
如果后续有多个类似的复杂查询,可以把查询条件的组装逻辑抽成独立的查询对象,让Service层更简洁:
// 定义查询对象 class InactiveUserQuery { static build(): Prisma.UserWhereInput { const oneMonthAgo = new Date(); oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1); return { isArchived: true, lastVisitedDate: { lt: oneMonthAgo } }; } } // Service层调用 @Injectable() export class UserService { constructor(private readonly userRepository: UserRepository) {} async getInactiveUsers(): Promise<User[]> { const conditions = InactiveUserQuery.build(); return this.userRepository.findAll(conditions); } }
这种方式适合查询逻辑复用的场景,能让Service层专注于业务流程,而不是条件组装。
2. 避免内存过滤的误区
别想着把所有用户查出来在Service层过滤——数据量小的时候没问题,但用户量上去后,这种做法会严重影响性能。一定要让数据库做过滤,也就是把条件传给仓储层,让Prisma生成高效的SQL查询。
内容的提问来源于stack exchange,提问作者eugenedrvnk
相关产品推荐
相关产品推荐

