第3章 Repository接口定义与分层设计

3.1 仓库层的职责边界

Repository 层是领域层与数据层之间的抽象桥梁,核心职责是:

  • 统一数据入口:对外隐藏本地缓存、远程API、数据库等具体数据源实现细节。
  • 数据聚合/协调:单一业务操作可能涉及多个数据源(如先从缓存读,再刷新网络),Repository 负责编排。
  • 业务无关性:Repository 只关心数据获取/持久化,不包含复杂的业务逻辑(业务逻辑属于 UseCase / ViewModel)。

下图清晰展示了三层之间的依赖方向与数据流向:

分层依赖原则:ViewModel → Repository(接口) → DataSource(接口) → 实现。
Repository 层分层架构图 ViewModel(Application 层) «interface» UserRepository 抽象仓库(业务层依赖目标) UserRepositoryImpl(Hilt 注入) implements UserRemoteDataSource UserLocalDataSource 数据源接口也需抽象定义,Repository 仅通过接口与数据源协作

3.2 UserRepository 接口定义

在 Android 项目中,UserRepository 通常定义为 Kotlin interface,方法使用 suspend 或返回 Flow 以便与协程无缝配合。以下是一个典型定义:

interface UserRepository {
    suspend fun getUsers(): List<User>
    suspend fun getUserById(id: String): User?
    suspend fun saveUser(user: User)
    suspend fun deleteUser(id: String)
    fun observeUsers(): Flow<List<User>>
}
设计提示:方法返回类型应面向“一次性操作”或“监听”。observeUsers() 返回 Flow 适用于实时观察本地数据变化;getUsers() 返回 suspend 适用于强制刷新或一次性查询。

3.3 抽象方法签名设计原则

每个方法签名需要满足以下四点:

原则说明反例
单一用途 一个方法只执行一个数据操作,不混用 CRUD fun getOrCreateUser(id: String): User
返回不可变数据 对外暴露 List<User> 而非 MutableList fun getUsers(): MutableList<User>
异常透明 方法不应吞异常,由调用方通过 Result 或 try-catch 处理 内部 catch 异常并返回 null
数据源无关 参数和返回值不暴露数据源细节(如 SQL 查询条件、URL 参数) fun getUserByIdFromRemote(id: String): User

3.4 分层架构视角

从架构层面看,Repository 接口是依赖倒置的体现:高层(ViewModel)不依赖低层(Retrofit/Room 实现),而是依赖抽象接口,实现类通过 Hilt 注入。具体职责划分如下:

  • Repository 接口:定义业务场景所需的数据契约,与具体数据源无关。
  • Repository 实现:组合本地/远程 DataSource,处理缓存策略、错误重试等数据层逻辑。
  • DataSource 接口:分别描述本地存储和网络 API 的原始操作。
  • DataSource 实现:对接 Room DAO 或 Retrofit Service。

3.5 设计误区与最佳实践

常见错误:将业务逻辑塞入 Repository
示例:fun getUserWithValidation(id: String): User? { if (id.isBlank()) return null ... }
正确做法:Repository 只关心数据获取,输入校验应放在 ViewModel 或 UseCase 层。
最佳实践:为每一个功能模块定义一个 Repository 接口
例如:UserRepository、OrderRepository、AuthRepository,每个接口只负责单一领域的数据操作,保持接口细化,便于单元测试和 Hilt 绑定。