1. Repository模式核心概念与离线场景适用性
1.1 模式定义与架构本质
定义:Repository 是在领域层定义的一种机制,负责封装存储、检索和检索行为,它模拟了一个集合(Collection)的概念。这是 Eric Evans 在《领域驱动设计》中提出的核心模式。
架构本质:Repository 并不是简单地与数据库交互,而是一种将领域实体与聚合根从具体的数据持久化技术(SQL、NoSQL、文件系统、缓存)中解耦的策略。这使得领域逻辑能够专注于业务规则,而不必关心数据在何处存储或如何读取。
离线关联:在离线环境下,数据源可能是本地的 SQLite、文件或内存缓存。Repository 就像一个统一的数据管理员,业务代码无需感知此刻数据是来自远程服务器还是本地缓存。
核心价值: 引入抽象屏障,使得领域层无需依赖具体数据库驱动或网络框架。在线与离线数据源的切换对业务逻辑透明。
1.2 离线场景下的数据需求挑战
- 网络不可达:用户在弱网或无网状态下操作时,应用必须优雅降级,不能崩溃或无限期挂起。
- 数据新鲜度:必须定义策略是“本地缓存优先”还是“远程获取优先”,并处理缓存过期问题。
- 写入冲突:离线环境下用户对数据进行修改,网络恢复后需要协调这些变更,避免数据覆盖或丢失。
- 存储容量:移动端或边缘设备存储空间有限,需要制定数据驱逐策略(LRU、TTL)。
1.3 领域层与数据层分离的架构意义
依赖倒置原则(DIP):高层模块(领域)不应依赖于低层模块(数据基础设施)。两者都应依赖于抽象。Repository 模式正是这一原则的典型应用。
信任链路:领域层信任 Repository 接口定义的集合语义,数据层信任 Repository 实现规范。接口由领域层定义,由数据层遵守。
控制流与数据流:控制流从领域层流经 Repository 到达数据源,但依赖流方向相反(领域层定义接口,数据层实现接口)。这使得领域层完全独立于数据技术。
1.4 核心组件职责与边界
| 组件 | 核心职责 | 边界位置 | 数据流示例 |
|---|---|---|---|
| 领域实体/聚合 | 封装业务状态与行为。 | 纯内存对象,属于领域层。 | 向 Repository 发送 save(user) 或 findById(id) |
| Repository 接口 | 定义集合语义(CRUD 操作)。 | 属于领域层,不引用数据源 API。 | interface UserRepository { find, save, delete } |
| Repository 实现 | 将接口调用路由到具体数据源。 | 属于数据基础设施层。 | 转发 findById 到 CacheDataSource 或 ApiDataSource |
| 本地数据源 | 管理离线存储(SQLite, File, MMKV)。 | 数据层。 | 返回本地数据或缓存远程数据。 |
| 远程数据源 | 管理网络请求(REST, GraphQL)。 | 数据层。 | 用户在线时获取最新数据。 |
1.5 架构方案权衡与风险点
- 选型权衡:
- 纯粹的抽象会带来轻微的运行时调度开销。
- 维护多套实现(在线/离线)会增加前期开发成本。
- 接口粒度(粗粒度 vs. 细粒度)直接影响复杂性与灵活性。
- 风险点:
- 泄漏抽象:Repository 的实现细节泄漏到领域层。例如领域层需要关心
FetchPolicy.REMOTE_FIRST或序列化方式。 - 过度设计:对于只有远程数据库且无离线需求的简单 CRUD 场景,强行引入多层抽象反而增加维护成本。
- 同步灾难:如果离线写入冲突未能妥善处理(如未使用冲突检测策略),会造成数据永久不一致。
- 泄漏抽象:Repository 的实现细节泄漏到领域层。例如领域层需要关心
架构警告: 不要为了让 Repository 模式工作而将领域实体退化为仅包含 Getter/Setter 的 DTO。领域实体应包含职责内聚的业务行为。Repository 的职责是返回领域对象,而不是原始数据集或数据行。
1.6 落地步骤与验收标准
落地步骤:
- 识别聚合:在领域模型中明确聚合根与边界上下文。
- 定义接口:在领域层模块中定义 Repository 接口,方法签名使用领域对象。
- 抽象数据源:定义数据源接口(例如
UserLocalDataSource、UserRemoteDataSource)。 - 实现组装:在数据基础设施层实现 Repository 接口,组合调用各种数据源。
- 依赖注入:通过 IoC 容器将具体实现挂接到领域层。
验收标准:
- 领域层代码零引用数据库驱动、网络库或具体序列化框架。
- 切换数据源时(例如从内存切换到 SQLite),无需修改领域层任何代码。
- 领域逻辑可独立测试,无需搭建数据库或网络 Mock 环境。