You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Clean架构下Repository与DataSource实现选型及命名困惑

Clean Architecture中Repository与DataSource的实现困惑

我正在当前项目中实现Clean Architecture,在Repository、DataSource相关术语及实现上存在困惑。我希望使用内存仓库测试useCases,聚焦业务逻辑,但对相关概念及实现方式存疑。

我定义了基础DataSource接口如下:

export interface DataSource<T> {    
    create(request: any): Promise<T>

    // more methods here but removed for simplicity
}

还定义了PatientRepository接口:

export interface PatientRepository {
    createPatient(patient: Patient): Promise<void>

    // more methods here but removed for simplicity
}

生产环境将使用Sequelize,单元测试则用本地实体数组。我纠结于PatientRepository的实现方案:

方案1

创建实现PatientRepository的PatientSequelizeRepository,继承实现DataSource契约的抽象SequelizeRepository;同时创建PatientInMemoryRepository,继承抽象InMemoryRepository。测试时用PatientInMemoryRepository,生产用PatientSequelizeRepository。

方案2

创建一个实现PatientRepository的PatientRepositoryImpl,通过构造函数注入DataSource,同时实现通用的InMemoryDataSource和SequelizeDataSource。默认注入SequelizeDataSource,也可替换其他数据源:

export class PatientRepositoryImpl implements PatientRepository {
    constructor(dataSource: DataSource<Patient>) { }
    
    async createPatient(patient: Patient) {
       return this.dataSource.create(patient)
    }
}

export const patientRepository = new PatientRepositoryImpl(new SequelizeDataSource())

我对命名存疑:是否应为Patient创建专属的PatientSequelizeDataSource和PatientInMemoryDataSource?哪种方案更优,是否有其他方案?


解答

方案对比与选型

核心依据Clean Architecture的依赖倒置原则:高层业务逻辑(UseCase)只依赖抽象(Repository接口),底层数据实现依赖高层抽象,而非反过来。

方案2更贴合架构设计

方案2的优势非常明显:

  • PatientRepositoryImpl仅依赖抽象接口,完全不耦合具体持久化技术,符合依赖倒置
  • 测试时只需替换为InMemoryDataSource<Patient>,就能彻底隔离外部数据库,专注测试业务逻辑
  • 后续切换ORM(如TypeORM)或存储方式时,只需新增对应DataSource<T>实现,无需修改Repository和上层业务代码

方案1的问题:

  • 每个实体都要编写两套Repository实现,重复代码多,维护成本高
  • Repository直接继承DataSource实现,混淆了职责边界——Repository负责业务数据编排,DataSource才是具体数据读写的组件

命名建议

不需要为Patient创建专属的PatientSequelizeDataSource和PatientInMemoryDataSource:

  • 泛型DataSource<T>已经明确了处理的实体类型,SequelizeDataSource<Patient>语义清晰且简洁
  • 后续新增其他实体(如Doctor)时,可直接复用SequelizeDataSource<Doctor>,避免重复造轮子
  • 若某个实体需要特殊的数据源逻辑(比如Patient有复杂的关联查询),再单独创建专属数据源即可,符合**YAGNI(You Ain't Gonna Need It)**原则

其他优化方案

如果项目中有多个Repository,可以进一步抽象通用Repository实现,减少重复代码:

// 通用基础Repository实现
export abstract class BaseRepositoryImpl<T> implements BaseRepository<T> {
  constructor(protected dataSource: DataSource<T>) {}

  async create(entity: T): Promise<T> {
    return this.dataSource.create(entity);
  }

  // 其他通用CRUD方法...
}

// Patient专属Repository继承通用实现,扩展业务方法
export class PatientRepositoryImpl extends BaseRepositoryImpl<Patient> implements PatientRepository {
  async getPatientWithMedicalRecords(id: string): Promise<Patient> {
    // 实现Patient专属的业务查询逻辑
  }
}

这种方式既保留了通用CRUD的复用性,又能让每个Repository专注于自身业务需求。

内容的提问来源于stack exchange,提问作者z0mbieKale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 06:10:59