1. 项目概述
在Flutter应用开发中,数据层的混乱管理一直是困扰开发者的痛点。Repository模式作为一种经典架构设计,能够有效解决数据源耦合、业务逻辑污染等问题。我在最近的一个电商类Flutter项目中,深度实践了Repository模式的数据层抽象方案,成功将API、本地数据库和缓存等不同数据源统一管理,使代码复用率提升40%以上。
这个方案最核心的价值在于:通过抽象接口隔离具体实现,业务层无需关心数据来自网络请求还是SQLite数据库。当需要切换数据源或修改实现逻辑时,只需调整Repository内部代码,上层调用完全不受影响。下面我将从设计思路到具体实现,完整分享这套经过实战检验的架构方案。
2. 核心设计思路
2.1 模式选型依据
为什么选择Repository而不是其他模式?在对比了MVVM、Redux等方案后,我发现:
- 单一职责原则:每个Repository只负责一个领域的数据访问
- 抽象隔离:通过接口定义契约,隐藏数据获取细节
- 可测试性:Mock接口实现方便单元测试
- 可扩展性:新增数据源只需实现接口,不改动业务逻辑
2.2 分层架构设计
典型的三层结构如下:
业务逻辑层 → Repository接口层 → 具体数据源实现 (抽象屏障)以用户模块为例:
// 业务层调用 final user = await userRepository.getUser(123); // 接口定义 abstract class UserRepository { Future<User> getUser(int id); } // 网络实现 class ApiUserRepository implements UserRepository { final ApiClient client; @override Future<User> getUser(int id) async { final json = await client.get('/users/$id'); return User.fromJson(json); } }3. 具体实现方案
3.1 基础接口设计
定义通用CRUD接口模板:
abstract class BaseRepository<T> { Future<T?> get(int id); Future<List<T>> getAll(); Future<int> create(T item); Future<bool> update(T item); Future<bool> delete(int id); }3.2 多数据源协调
实际项目往往需要组合多个数据源。例如商品详情页:
class ProductRepositoryImpl implements ProductRepository { final ApiClient _api; final LocalDb _db; final MemoryCache _cache; @override Future<Product> getProduct(int id) async { // 1. 先查缓存 if (_cache.has(id)) return _cache.get(id); // 2. 查本地数据库 final localData = await _db.queryProduct(id); if (localData != null) { _cache.set(id, localData); return localData; } // 3. 最后请求网络 final remoteData = await _api.getProduct(id); await _db.saveProduct(remoteData); _cache.set(id, remoteData); return remoteData; } }3.3 依赖注入配置
使用get_it实现依赖管理:
final getIt = GetIt.instance; void setupRepositories() { getIt.registerSingleton<ApiClient>(DioClient()); getIt.registerSingleton<LocalDb>(SqfliteDb()); getIt.registerFactory<UserRepository>( () => UserRepositoryImpl( getIt<ApiClient>(), getIt<LocalDb>() ) ); }4. 高级应用技巧
4.1 流式数据支持
通过Stream实现实时数据更新:
abstract class UserRepository { Stream<User?> watchUser(int id); } class UserRepositoryImpl { final _controller = StreamController<User?>(); @override Stream<User?> watchUser(int id) { _fetchUserPeriodically(id); // 定时轮询 return _controller.stream; } }4.2 复合Repository
处理关联数据查询:
class OrderRepository { Future<OrderWithDetails> getOrderWithDetails(int id) async { final order = await _orderRepo.get(id); final items = await _itemRepo.getByOrder(id); return OrderWithDetails(order, items); } }5. 性能优化实践
5.1 缓存策略设计
三级缓存实现方案:
- 内存缓存:使用LRU算法,存储最热数据
- 本地存储:重要业务数据持久化
- 网络请求:设置合理缓存头
class CachedRepository { final _memory = LRUCache(maxSize: 100); final _local = HiveStorage(); Future<Data> _getWithCache(int id) async { if (_memory.contains(id)) return _memory.get(id); final localData = await _local.get(id); if (localData != null) { _memory.set(id, localData); return localData; } final remoteData = await _api.get(id); await _local.save(id, remoteData); _memory.set(id, remoteData); return remoteData; } }5.2 批量请求处理
针对列表页的优化方案:
Future<List<Product>> getProducts(List<int> ids) async { final uncachedIds = ids.where((id) => !_cache.has(id)).toList(); if (uncachedIds.isNotEmpty) { final batchData = await _api.getBatchProducts(uncachedIds); _cache.addAll(batchData); } return ids.map((id) => _cache.get(id)).toList(); }6. 测试策略
6.1 单元测试方案
使用Mockito测试Repository:
class MockApiClient extends Mock implements ApiClient {} void main() { late UserRepository repo; late MockApiClient mockApi; setUp(() { mockApi = MockApiClient(); repo = ApiUserRepository(mockApi); }); test('getUser returns data', () async { when(mockApi.get('/users/1')).thenAnswer( (_) async => {'id': 1, 'name': 'Test'} ); final user = await repo.getUser(1); expect(user?.name, 'Test'); }); }6.2 集成测试要点
测试多数据源协作:
void main() { late LocalDb testDb; late UserRepository repo; setUp(() async { testDb = await setupTestDb(); repo = UserRepositoryImpl( MockApiClient(), testDb ); }); test('fallback to local when api fails', () async { await testDb.saveUser(User(id: 1, name: 'Local')); final user = await repo.getUser(1); expect(user.name, 'Local'); }); }7. 常见问题解决
7.1 循环依赖处理
当Repository之间需要互相调用时:
// 错误示例 class OrderRepository { final ProductRepository products; // 循环依赖 } // 正确方案 class OrderRepository { Future<OrderWithProducts> getOrderWithProducts(int id) { final order = await _getOrder(id); final products = await _productService.getProducts(order.itemIds); return OrderWithProducts(order, products); } }7.2 线程安全保证
Dart的Isolate使用建议:
class ThreadSafeRepository { final _lock = Lock(); Future<Data> safeUpdate(int id) async { await _lock.synchronized(() async { final data = await _getData(id); await _updateData(data.copyWith(updated: true)); }); } }8. 项目适配建议
8.1 中小型项目优化
简化方案:
- 合并基础Repository接口
- 使用单例全局访问
- 简化缓存层级
// 简易实现 final userRepo = UserRepository._internal(); class UserRepository { static final instance = userRepo; Future<User> getUser(int id) async { // 简单缓存实现 if (_cache[id] != null) return _cache[id]; return _cache[id] = await _api.getUser(id); } }8.2 大型项目扩展
进阶方案:
- 按领域分库
- 添加中间件层
- 实现自动代码生成
// 领域划分示例 lib/ repositories/ user/ - user_repository.dart - user_entity.dart - user_mapper.dart product/ - product_repository.dart - ...在实践过程中,我发现Repository模式特别适合需要长期维护的商业项目。当业务逻辑变得越来越复杂时,良好的数据层抽象就像一道防火墙,阻止混乱扩散到整个代码库。建议在项目初期就建立规范的Repository结构,这比后期重构要轻松得多。