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

为什么整洁架构通常要分离Repository与DataSource的实现?

为什么整洁架构示例中要给Retrofit/Room接口套一层DataSource包装?

在整洁架构的演示示例中,我们经常看到这样的设计:明明Retrofit的MyApi、Room的MyDao本身已是可通过依赖注入替换实例的接口,却仍要额外封装一层RemoteDataSource/LocalDataSource,实现仅做简单转发,而非让Repository直接依赖这些框架接口。这种设计的核心原因如下:

示例代码对比

// Retrofit框架接口
interface MyApi {
   @GET("somePath")
   fun getSome() : SomeResponse
}

// Room框架接口
@Dao
interface MyDao {
   @Query("select * from mytable")
   fun getSome() : List<SomeEntity>
}

// 方式1:大多数示例的实现
interface RemoteDataSource {
   fun getSome() : SomeResponse
}
interface LocalDataSource {
   fun getSome() : List<SomeEntity>
}

class RemoteDataSourceImpl @Inject constructor(private val api : MyApi) : RemoteDataSource {
    override fun getSome() = api.getSome()
}

class LocalDataSourceImpl @Inject constructor(private val dao : MyDao) : LocalDataSource {
    override fun getSome() = dao.getSome()
}

class MyRepositoryImpl @Inject constructor(private val remote : RemoteDataSource, private val local : LocalDataSource) : MyRepository {
   override fun mySomeGetFunction() {
      return MySomeEntity(entity1 = remote.getSome().data, entity2 = local.getSome().firstOrNull())
   }
}

// 方式2:直接依赖框架接口的实现
class MyRepositoryImpl @Inject constructor(private val api : MyApi, private val dao : MyDao) : MyRepository {
   override fun mySomeGetFunction() {
      return MySomeEntity(entity1 = api.getSome().data, entity2 = dao.getSome().firstOrNull())
   }
}

核心设计原因

  • 隔离框架细节,坚守依赖倒置原则
    整洁架构的核心要求是领域层(包括Repository定义)不依赖任何外部框架。MyApi和MyDao本质是绑定了Retrofit/Room的框架特定接口,带有框架专属注解和返回类型约束。如果直接让Repository依赖它们,一旦后续更换框架(比如把Retrofit换成OkHttp手写实现,或Room换成其他ORM),就必须修改Repository的依赖和逻辑,违反了「依赖抽象而非具体实现」的原则。而RemoteDataSource是我们自主定义的、完全贴合业务需求的抽象,和框架无关,底层更换实现时,Repository代码无需改动。

  • 拆分职责,避免Repository臃肿
    实际业务中,数据操作不会只有单一接口调用:比如远程请求需要统一处理异常、添加重试逻辑、参数转换;本地数据可能需要做实体映射、缓存有效期校验。这些框架层面或通用数据逻辑放在RemoteDataSourceImpl/LocalDataSourceImpl中,能让Repository只专注于业务逻辑的组合(比如何时读本地、何时请求远程),符合单一职责原则。后续修改数据层的通用逻辑时,无需侵入Repository代码。

  • 简化测试,降低测试成本
    虽然MyApi是接口可以Mock,但Mock框架接口时需要适配框架的特性:比如Retrofit的Call类型、Room的Flow异步返回。而RemoteDataSource的返回类型是业务相关的SomeResponse,Mock时只需返回固定业务数据,无需配置MockWebServer或测试数据库,测试代码更简洁,也能避免框架特性带来的测试复杂度。

  • 统一数据层抽象,适配多数据源差异
    当项目存在多类数据源(比如同时有Retrofit接口、第三方SDK、SharedPreferences),用RemoteDataSource/LocalDataSource统一抽象后,Repository面对的是一致的调用风格。比如第三方SDK是回调式、SharedPreferences是同步读取,都可以在DataSource层统一转换成Repository需要的调用方式(比如suspend函数、Flow),Repository无需关心不同数据源的调用差异。

内容的提问来源于stack exchange,提问作者H.Kim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 10:31:08