This commit is contained in:
petrovyuri
2025-01-21 14:24:31 +03:00
parent e7b2c31e86
commit 17d096baac
96 changed files with 3575 additions and 0 deletions

6
lib/di/di_base_repo.dart Normal file
View File

@@ -0,0 +1,6 @@
/// Миксин репозитория в приложении.
/// Каждый интерфейс репозитория в приложении должен подмешивать текущий класс
mixin class DiBaseRepo {
/// Наименование репозитория
String get name => 'DiBaseRepo';
}

103
lib/di/di_container.dart Normal file
View File

@@ -0,0 +1,103 @@
import 'package:app_services/app_services.dart';
import 'package:friflex_starter/app/app_config/app_config.dart';
import 'package:friflex_starter/app/app_config/i_app_config.dart';
import 'package:friflex_starter/app/app_env.dart';
import 'package:friflex_starter/app/http/app_http_client.dart';
import 'package:friflex_starter/app/http/i_http_client.dart';
import 'package:friflex_starter/di/di_repositories.dart';
import 'package:friflex_starter/di/di_typedefs.dart';
import 'package:friflex_starter/features/debug/i_debug_service.dart';
import 'package:i_app_services/i_app_services.dart';
/// {@template dependencies_container}
/// Контейнер для зависимостей
/// {@macro composition_process}
/// {@endtemplate}
final class DiContainer {
/// {@macro dependencies_container}
DiContainer({required this.env, required IDebugService dService})
: debugService = dService;
final AppEnv env;
/// Сервис для отладки, получаем из конструктора
late final IDebugService debugService;
/// Сервис для работы с путями
late final IPathProvider pathProvider;
/// Конфигурация приложения
late final IAppConfig appConfig;
/// Сервис для работы с локальным хранилищем
late final ISecureStorage secureStorage;
/// Сервис для работы с HTTP запросами
late final IHttpClient httpClient;
late final DiRepositories repositories;
/// Метод для инициализации зависимостей
Future<void> init({
required OnProgress onProgress,
required OnComplete onComplete,
required OnError onError,
}) async {
// Инициализация сервисов
await _initServices(
onComplete: onComplete,
onError: onError,
onProgress: onProgress,
);
// Инициализация репозиториев
repositories = DiRepositories();
repositories.init(
onProgress: onProgress,
onComplete: onComplete,
onError: onError,
diContainer: this,
);
onComplete('Инициализация зависимостей завершена!');
}
/// Метод для инициализации сервисов
Future<void> _initServices({
required OnComplete onComplete,
required OnError onError,
required OnProgress onProgress,
}) async {
appConfig = switch (env) {
AppEnv.dev => AppConfigDev(),
AppEnv.prod => AppConfigProd(),
AppEnv.stage => AppConfigStage()
};
httpClient = AppHttpClient(
debugService: debugService,
appConfig: appConfig,
);
try {
pathProvider = AppPathProvider();
onProgress(AppPathProvider.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации ${IPathProvider.name}',
error: error,
stackTrace: stackTrace,
);
}
try {
secureStorage = AppSecureStorage(secretKey: appConfig.secretKey);
onProgress(AppSecureStorage.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации ${ISecureStorage.name}',
error: error,
stackTrace: stackTrace,
);
}
}
}

115
lib/di/di_repositories.dart Normal file
View File

@@ -0,0 +1,115 @@
import 'package:friflex_starter/app/app_env.dart';
import 'package:friflex_starter/di/di_base_repo.dart';
import 'package:friflex_starter/di/di_container.dart';
import 'package:friflex_starter/di/di_typedefs.dart';
import 'package:friflex_starter/features/auth/data/repository/auth_mock_repository.dart';
import 'package:friflex_starter/features/auth/data/repository/auth_repository.dart';
import 'package:friflex_starter/features/auth/domain/repository/i_auth_repository.dart';
import 'package:friflex_starter/features/main/data/repository/main_mock_repository.dart';
import 'package:friflex_starter/features/main/data/repository/main_repository.dart';
import 'package:friflex_starter/features/main/domain/repository/i_main_repository.dart';
/// Список названий моковых репозиториев, которые должны быть подменены
/// для использования в сборке stage окружения
///
/// Для того, чтобы репозиторий был автоматически подменен на моковый в stage
/// сборке, необходимо в этом списке указать название мокового репозитория,
/// обращаясь к соответствующему полю name.
///
/// Пример:
/// ```
/// [ AuthCheckRepositoryMock().name, ]
/// ```
final List<String> _mockReposToSwitch = [];
/// Класс для инициализации репозиториев в приложении
///
/// По умолчанию репозиторию присваивается моковая реализация.
/// В зависимости от окружения либо выполняется подмена репозиторий,
/// либо используется моковый.
final class DiRepositories {
/// Интерфейс для работы с репозиторием авторизации
late final IAuthRepository authRepository;
/// Интерфейс для работы с репозиторием главного сервиса
late final IMainRepository mainRepository;
/// Метод для инициализации репозиториев в приложении
///
/// Принимает:
/// - [onProgress] - обратный вызов при прогрессе
/// - [onComplete] - обратный вызов при успешной инициализации
/// - [diContainer] - контейнер зависимостей
void init({
required OnProgress onProgress,
required OnComplete onComplete,
required OnError onError,
required DiContainer diContainer,
}) {
try {
//Инициализация репозитория авторизации
authRepository = lazyInitRepo<IAuthRepository>(
mockFactory: AuthMockRepository.new,
mainFactory: () => AuthRepository(
httpClient: diContainer.httpClient,
),
onProgress: onProgress,
environment: diContainer.env,
);
onProgress(authRepository.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации репозитория $IAuthRepository',
error: error,
stackTrace: stackTrace,
);
}
try {
// Инициализация репозитория сервиса управления токеном доступа
mainRepository = lazyInitRepo<IMainRepository>(
mockFactory: MainMockRepository.new,
mainFactory: () => MainRepository(
httpClient: diContainer.httpClient,
),
onProgress: onProgress,
environment: diContainer.env,
);
onProgress(mainRepository.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации репозитория $IMainRepository',
error: error,
stackTrace: stackTrace,
);
}
onComplete(
'Инициализация репозиториев завершена! Было подменено репозиториев - ${_mockReposToSwitch.length} (${_mockReposToSwitch.join(', ')})',
);
}
/// Метод для ленивой инициализации конкретного репозитория по типу [Т].
/// В зависимости от окружения инициализируется моковый или сетевой репозиторий.
///
/// Принимает:
/// - [mockFactory] - функция - фабрика для инициализации репозитория для управления моковыми запросами
/// - [mainFactory] - функция - фабрика для инициализации основного репозиторий
/// - [onProgress] - обратный вызов при прогрессе
T lazyInitRepo<T extends DiBaseRepo>({
required AppEnv environment,
required T Function() mainFactory,
required T Function() mockFactory,
required OnProgress onProgress,
}) {
final repo = switch (environment) {
AppEnv.dev => mockFactory(),
AppEnv.prod => mainFactory(),
AppEnv.stage => _mockReposToSwitch.contains(mockFactory().name)
? mockFactory()
: mainFactory(),
};
onProgress(repo.name);
return repo;
}
}

12
lib/di/di_typedefs.dart Normal file
View File

@@ -0,0 +1,12 @@
/// Обратный вызов при ошибки инициализации
typedef OnError = void Function(
String message, {
Object? error,
StackTrace? stackTrace,
});
/// Обратный вызов при прогрессе
typedef OnProgress = void Function(String name);
/// Обратный вызов при успешной инициализации
typedef OnComplete = void Function(String msg);