第3章 Repository接口定义与分层设计
3.1 仓库层的职责边界
Repository 层是领域层与数据层之间的抽象桥梁,核心职责是:
- 统一数据入口:对外隐藏本地缓存、远程API、数据库等具体数据源实现细节。
- 数据聚合/协调:单一业务操作可能涉及多个数据源(如先从缓存读,再刷新网络),Repository 负责编排。
- 业务无关性:Repository 只关心数据获取/持久化,不包含复杂的业务逻辑(业务逻辑属于 UseCase / ViewModel)。
下图清晰展示了三层之间的依赖方向与数据流向:
分层依赖原则:ViewModel → Repository(接口) → DataSource(接口) → 实现。
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
示例:
正确做法:Repository 只关心数据获取,输入校验应放在 ViewModel 或 UseCase 层。
示例:
fun getUserWithValidation(id: String): User? { if (id.isBlank()) return null ... }正确做法:Repository 只关心数据获取,输入校验应放在 ViewModel 或 UseCase 层。
最佳实践:为每一个功能模块定义一个 Repository 接口
例如:
例如:
UserRepository、OrderRepository、AuthRepository,每个接口只负责单一领域的数据操作,保持接口细化,便于单元测试和 Hilt 绑定。