Что такое репозиторий (Repository) в архитектуре Flutter-приложения?

Ответ

Репозиторий — это паттерн уровня данных, который выступает в роли единого источника правды для доменного слоя. Он абстрагирует детали получения данных (из сети, локальной БД, кэша в памяти), предоставляя чистый API для бизнес-логики.

Зачем он нужен во Flutter?

  • Изоляция: Бизнес-логика (Use Cases/Bloc/Cubit) не знает, откуда берутся данные — из REST API, GraphQL, Firebase или SharedPreferences.
  • Гибкость: Можно легко подменить источник данных (например, использовать мок для тестов) или добавить новый (например, кэш Hive поверх Firestore).
  • Централизация логики данных: Повторяющиеся операции (кэширование, повторные запросы при ошибке, преобразование DTO в модель) живут в одном месте.

Пример реализации на Dart/Flutter:

// Доменная модель
class User {
  final String id;
  final String name;
  User({required this.id, required this.name});
}

// Абстракция репозитория
abstract class UserRepository {
  Future<User> getUser(String id);
  Future<List<User>> searchUsers(String query);
}

// Конкретная реализация
class UserRepositoryImpl implements UserRepository {
  final UserRemoteDataSource _remoteDataSource;
  final UserLocalDataSource _localDataSource;

  UserRepositoryImpl(this._remoteDataSource, this._localDataSource);

  @override
  Future<User> getUser(String id) async {
    // Сначала проверяем локальное хранилище
    try {
      final localUser = await _localDataSource.getUser(id);
      return localUser;
    } catch (_) {
      // Если нет локально, идем в сеть и сохраняем результат
      final remoteUser = await _remoteDataSource.getUser(id);
      await _localDataSource.saveUser(remoteUser);
      return remoteUser;
    }
  }

  @override
  Future<List<User>> searchUsers(String query) {
    // Для поиска всегда идем в сеть, но можно добавить и кэширование
    return _remoteDataSource.searchUsers(query);
  }
}

В Clean Architecture или BLoC репозиторий — это мост между Data Layer и Domain Layer.

Ответ 18+ 🔞

Да ты посмотри, какая хитрая жопа этот репозиторий! Ну представь себе, у тебя в приложении бизнес-логика — она как царь и бог, должна думать о высоком, а не о том, откуда там данные лезут: из интернета, из базы или вообще из воздуха. Вот репозиторий и есть такой верный холоп, который бегает, всё добывает и подаёт на блюдечке.

Зачем он, спрашивается, во Flutter?

  • Чтобы не париться: Твои Use Cases или Bloc'и вообще в рот берут, откуда данные. Им похуй — дали модель, и хорошо. А репозиторий сам разберётся, пойти ли в сеть или в кэш посмотреть.
  • Гибкость — овердохуища: Захотел поменять API с REST на GraphQL? Без проблем, меняешь одну реализацию репозитория, и вся бизнес-логика даже не чихнёт. Тесты писать? Подсовываешь мок-репозиторий, который всегда возвращает то, что надо, и не надо интернет эмулировать.
  • Всё в одном месте: Вся эта муть с кэшированием, повторами запросов если обосралось, преобразованием сырых JSON'ов в нормальные модели — всё это сидит в репозитории, а не размазано по всему коду. Красота, ёпта!

Смотри, как это выглядит в коде:

// Это наша доменная модель, священная корова
class User {
  final String id;
  final String name;
  User({required this.id, required this.name});
}

// Абстракция репозитория. Контракт, что он должен уметь.
abstract class UserRepository {
  Future<User> getUser(String id);
  Future<List<User>> searchUsers(String query);
}

// А вот и конкретная реализация, где вся магия
class UserRepositoryImpl implements UserRepository {
  final UserRemoteDataSource _remoteDataSource; // Источник из сети
  final UserLocalDataSource _localDataSource;   // Источник из базы

  UserRepositoryImpl(this._remoteDataSource, this._localDataSource);

  @override
  Future<User> getUser(String id) async {
    // Сначала честно пытаемся взять из локального хранилища
    try {
      final localUser = await _localDataSource.getUser(id);
      return localUser;
    } catch (_) {
      // Если локально нихуя нет или ошибка — летим в сеть
      final remoteUser = await _remoteDataSource.getUser(id);
      // И чтобы два раза не вставать, сохраняем результат локально
      await _localDataSource.saveUser(remoteUser);
      return remoteUser;
    }
  }

  @override
  Future<List<User>> searchUsers(String query) {
    // Поиск — штука живая, тут обычно всегда свежие данные из сети нужны
    return _remoteDataSource.searchUsers(query);
  }
}

Короче, в тех модных архитектурах вроде Clean или в BLoC, репозиторий — это такой мостик, ебать мои старые костыли, между миром сырых данных (Data Layer) и миром чистой бизнес-логики (Domain Layer). Без него — распиздяйство и бардак.