← Назад к вопросам
Что такое репозиторий?
2.0 Middle🔥 182 комментариев
#Архитектура Flutter#ООП и паттерны
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Репозиторий (Repository)
Репозиторий — это слой абстракции, который инкапсулирует логику доступа к данным. Репозиторий скрывает детали того, откуда берутся данные (база данных, API, локальное хранилище), и предоставляет простой интерфейс для работы с ними.
Архитектура с Repository
┌─────────────────────┐
│ Presentation │ UI, BLoC, Screens
└──────────┬──────────┘
↓
┌─────────────────────┐
│ Interactor/UseCase │ Бизнес-логика
└──────────┬──────────┘
↓
┌─────────────────────┐
│ Repository │ <- Абстракция
└──────────┬──────────┘
↓
┌─────────────────────┐
│ Data Sources │ API, Database, Cache
└─────────────────────┘
Repository получает данные из одного или нескольких источников и предоставляет их в единообразном формате.
Простой пример: Repository для пользователей
// Модель
class User {
final String id;
final String name;
final String email;
User({
required this.id,
required this.name,
required this.email,
});
// Преобразование в JSON
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'email': email,
};
// Создание из JSON
factory User.fromJson(Map<String, dynamic> json) => User(
id: json['id'],
name: json['name'],
email: json['email'],
);
}
// Абстрактный интерфейс Repository
abstract class UserRepository {
Future<User> getUserById(String id);
Future<List<User>> getAllUsers();
Future<void> createUser(User user);
Future<void> updateUser(User user);
Future<void> deleteUser(String id);
}
// Конкретная реализация: получение данных с API
class UserApiRepository implements UserRepository {
final String baseUrl = 'https://api.example.com';
@override
Future<User> getUserById(String id) async {
try {
// Имитация HTTP запроса
print('Fetching user from API: $id');
// final response = await http.get(Uri.parse('$baseUrl/users/$id'));
// return User.fromJson(jsonDecode(response.body));
return User(id: id, name: 'John', email: 'john@example.com');
} catch (e) {
throw Exception('Failed to fetch user: $e');
}
}
@override
Future<List<User>> getAllUsers() async {
try {
print('Fetching all users from API');
return [
User(id: '1', name: 'John', email: 'john@example.com'),
User(id: '2', name: 'Jane', email: 'jane@example.com'),
];
} catch (e) {
throw Exception('Failed to fetch users: $e');
}
}
@override
Future<void> createUser(User user) async {
try {
print('Creating user on API');
// await http.post(...)
} catch (e) {
throw Exception('Failed to create user: $e');
}
}
@override
Future<void> updateUser(User user) async {
try {
print('Updating user on API');
} catch (e) {
throw Exception('Failed to update user: $e');
}
}
@override
Future<void> deleteUser(String id) async {
try {
print('Deleting user from API');
} catch (e) {
throw Exception('Failed to delete user: $e');
}
}
}
// Другая реализация: локальная база данных
class UserLocalRepository implements UserRepository {
// Имитация локальной БД
final List<User> _localDb = [];
@override
Future<User> getUserById(String id) async {
try {
print('Fetching user from local database: $id');
return _localDb.firstWhere((user) => user.id == id);
} catch (e) {
throw Exception('User not found: $e');
}
}
@override
Future<List<User>> getAllUsers() async {
print('Fetching all users from local database');
return _localDb;
}
@override
Future<void> createUser(User user) async {
print('Creating user in local database');
_localDb.add(user);
}
@override
Future<void> updateUser(User user) async {
print('Updating user in local database');
final index = _localDb.indexWhere((u) => u.id == user.id);
if (index >= 0) {
_localDb[index] = user;
}
}
@override
Future<void> deleteUser(String id) async {
print('Deleting user from local database');
_localDb.removeWhere((user) => user.id == id);
}
}
Repository с несколькими источниками данных
// Комбинированный Repository: сначала локальное хранилище, потом API
class UserCachedRepository implements UserRepository {
final UserApiRepository apiRepository;
final UserLocalRepository localRepository;
UserCachedRepository({
required this.apiRepository,
required this.localRepository,
});
@override
Future<User> getUserById(String id) async {
try {
// Сначала пытаемся получить из локального хранилища
print('Trying local database...');
return await localRepository.getUserById(id);
} catch (e) {
// Если не нашли, получаем с API
print('Not found locally, fetching from API...');
final user = await apiRepository.getUserById(id);
// Сохраняем в локальное хранилище
await localRepository.createUser(user);
return user;
}
}
@override
Future<List<User>> getAllUsers() async {
try {
return await localRepository.getAllUsers();
} catch (e) {
final users = await apiRepository.getAllUsers();
for (var user in users) {
await localRepository.createUser(user);
}
return users;
}
}
@override
Future<void> createUser(User user) async {
await apiRepository.createUser(user);
await localRepository.createUser(user);
}
@override
Future<void> updateUser(User user) async {
await apiRepository.updateUser(user);
await localRepository.updateUser(user);
}
@override
Future<void> deleteUser(String id) async {
await apiRepository.deleteUser(id);
await localRepository.deleteUser(id);
}
}
Использование Repository в Interactor
// Use Case (Interactor)
class GetUserUseCase {
final UserRepository repository;
GetUserUseCase({required this.repository});
Future<User> call(String userId) async {
// Бизнес-логика просто использует Repository
final user = await repository.getUserById(userId);
// Можно добавить обогащение данных
// Можно добавить валидацию
// Но детали доступа к данным скрыты
return user;
}
}
// BLoC использует Use Case
class UserBloc extends Bloc<UserEvent, UserState> {
final GetUserUseCase getUserUseCase;
UserBloc({required this.getUserUseCase})
: super(const UserState.initial()) {
on<GetUserEvent>(_onGetUser);
}
Future<void> _onGetUser(
GetUserEvent event,
Emitter<UserState> emit,
) async {
emit(const UserState.loading());
try {
final user = await getUserUseCase(event.userId);
emit(UserState.success(user));
} catch (e) {
emit(UserState.error(e.toString()));
}
}
}
Преимущества Repository паттерна
- Абстракция источника данных — код не зависит от того, откуда берутся данные
- Тестируемость — легко создать mock Repository для тестов
- Переиспользование — один Repository могут использовать разные Use Cases
- Кэширование — можно добавить слой кэша между API и БД
- Слабая связанность — изменения в API не влияют на бизнес-логику
- Централизованная логика — вся логика работы с данными в одном месте
Зависимость Injection
// При создании объектов передаём нужный Repository
final apiRepository = UserApiRepository();
final getUserUseCase = GetUserUseCase(repository: apiRepository);
final userBloc = UserBloc(getUserUseCase: getUserUseCase);
// Для тестов передаём mock
final mockRepository = MockUserRepository();
final testUseCase = GetUserUseCase(repository: mockRepository);
Repository — это фундаментальный паттерн в Clean Architecture, обеспечивающий разделение ответственности и гибкость архитектуры.