refactor: Repository 패턴 적용 및 Clean Architecture 완성
## 주요 변경사항 ### 🏗️ Architecture - Repository 패턴 전면 도입 (인터페이스/구현체 분리) - Domain Layer에 Repository 인터페이스 정의 - Data Layer에 Repository 구현체 배치 - UseCase 의존성을 Service에서 Repository로 전환 ### 📦 Dependency Injection - GetIt 기반 DI Container 재구성 (lib/injection_container.dart) - Repository 인터페이스와 구현체 등록 - Service와 Repository 공존 (마이그레이션 기간) ### 🔄 Migration Status 완료: - License 모듈 (6개 UseCase) - Warehouse Location 모듈 (5개 UseCase) 진행중: - Auth 모듈 (2/5 UseCase) - Company 모듈 (1/6 UseCase) 대기: - User 모듈 (7개 UseCase) - Equipment 모듈 (4개 UseCase) ### 🎯 Controller 통합 - 중복 Controller 제거 (with_usecase 버전) - 단일 Controller로 통합 - UseCase 패턴 직접 적용 ### 🧹 코드 정리 - 임시 파일 제거 (test_*.md, task.md) - Node.js 아티팩트 제거 (package.json) - 불필요한 테스트 파일 정리 ### ✅ 테스트 개선 - Real API 중심 테스트 구조 - Mock 제거, 실제 API 엔드포인트 사용 - 통합 테스트 프레임워크 강화 ## 기술적 영향 - 의존성 역전 원칙 적용 - 레이어 간 결합도 감소 - 테스트 용이성 향상 - 확장성 및 유지보수성 개선 ## 다음 단계 1. User/Equipment 모듈 Repository 마이그레이션 2. Service Layer 점진적 제거 3. 캐싱 전략 구현 4. 성능 최적화
This commit is contained in:
@@ -232,7 +232,7 @@ abstract class ScreenTestFramework {
|
||||
await testCase.setup?.call(testData);
|
||||
|
||||
// 테스트 실행
|
||||
await testCase.execute(testData);
|
||||
await testCase.call(testData);
|
||||
|
||||
// 검증
|
||||
await testCase.verify(testData);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:superport/data/datasources/remote/api_client.dart';
|
||||
import 'package:superport/data/datasources/remote/api_interceptor.dart';
|
||||
import 'package:superport/data/datasources/interceptors/api_interceptor.dart';
|
||||
import 'package:superport/data/datasources/remote/auth_remote_datasource.dart';
|
||||
import 'package:superport/data/datasources/remote/company_remote_datasource.dart';
|
||||
import 'package:superport/data/datasources/remote/equipment_remote_datasource.dart';
|
||||
@@ -24,29 +24,25 @@ import 'package:superport/domain/usecases/auth/login_usecase.dart';
|
||||
import 'package:superport/domain/usecases/company/create_company_usecase.dart';
|
||||
import 'package:superport/domain/usecases/company/delete_company_usecase.dart';
|
||||
import 'package:superport/domain/usecases/company/get_companies_usecase.dart';
|
||||
import 'package:superport/domain/usecases/company/get_company_usecase.dart';
|
||||
import 'package:superport/domain/usecases/company/get_company_detail_usecase.dart';
|
||||
import 'package:superport/domain/usecases/company/toggle_company_status_usecase.dart';
|
||||
import 'package:superport/domain/usecases/company/update_company_usecase.dart';
|
||||
import 'package:superport/domain/usecases/equipment/create_equipment_usecase.dart';
|
||||
import 'package:superport/domain/usecases/equipment/delete_equipment_usecase.dart';
|
||||
import 'package:superport/domain/usecases/equipment/equipment_in_usecase.dart';
|
||||
import 'package:superport/domain/usecases/equipment/equipment_out_usecase.dart';
|
||||
import 'package:superport/domain/usecases/equipment/get_equipment_usecase.dart';
|
||||
import 'package:superport/domain/usecases/equipment/get_equipments_usecase.dart';
|
||||
import 'package:superport/domain/usecases/equipment/update_equipment_usecase.dart';
|
||||
import 'package:superport/domain/usecases/license/create_license_usecase.dart';
|
||||
import 'package:superport/domain/usecases/license/delete_license_usecase.dart';
|
||||
import 'package:superport/domain/usecases/license/get_license_usecase.dart';
|
||||
import 'package:superport/domain/usecases/license/get_license_detail_usecase.dart';
|
||||
import 'package:superport/domain/usecases/license/get_licenses_usecase.dart';
|
||||
import 'package:superport/domain/usecases/license/update_license_usecase.dart';
|
||||
import 'package:superport/domain/usecases/user/create_user_usecase.dart';
|
||||
import 'package:superport/domain/usecases/user/delete_user_usecase.dart';
|
||||
import 'package:superport/domain/usecases/user/get_user_usecase.dart';
|
||||
import 'package:superport/domain/usecases/user/get_user_detail_usecase.dart';
|
||||
import 'package:superport/domain/usecases/user/get_users_usecase.dart';
|
||||
import 'package:superport/domain/usecases/user/update_user_usecase.dart';
|
||||
import 'package:superport/domain/usecases/warehouse_location/create_warehouse_location_usecase.dart';
|
||||
import 'package:superport/domain/usecases/warehouse_location/delete_warehouse_location_usecase.dart';
|
||||
import 'package:superport/domain/usecases/warehouse_location/get_warehouse_location_usecase.dart';
|
||||
import 'package:superport/domain/usecases/warehouse_location/get_warehouse_location_detail_usecase.dart';
|
||||
import 'package:superport/domain/usecases/warehouse_location/get_warehouse_locations_usecase.dart';
|
||||
import 'package:superport/domain/usecases/warehouse_location/update_warehouse_location_usecase.dart';
|
||||
import 'package:superport/services/auth_service.dart';
|
||||
@@ -56,6 +52,8 @@ import 'package:superport/services/license_service.dart';
|
||||
import 'package:superport/services/user_service.dart';
|
||||
import 'package:superport/services/warehouse_service.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:superport/core/storage/secure_storage.dart';
|
||||
|
||||
/// Real API 테스트 헬퍼
|
||||
///
|
||||
@@ -94,8 +92,11 @@ class RealApiTestHelper {
|
||||
},
|
||||
));
|
||||
|
||||
// SecureStorage 인스턴스 생성
|
||||
final secureStorage = SecureStorage();
|
||||
|
||||
// API 인터셉터 추가
|
||||
dio.interceptors.add(ApiInterceptor());
|
||||
dio.interceptors.add(ApiInterceptor(secureStorage));
|
||||
|
||||
// 로깅 인터셉터 추가 (디버그용)
|
||||
dio.interceptors.add(LogInterceptor(
|
||||
@@ -108,42 +109,47 @@ class RealApiTestHelper {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
// FlutterSecureStorage
|
||||
const flutterSecureStorage = FlutterSecureStorage();
|
||||
|
||||
// 의존성 등록 - 순서 중요!
|
||||
|
||||
// 1. 기본 인프라
|
||||
_testGetIt!.registerSingleton<Dio>(dio);
|
||||
_testGetIt!.registerSingleton<SharedPreferences>(prefs);
|
||||
_testGetIt!.registerSingleton<FlutterSecureStorage>(flutterSecureStorage);
|
||||
_testGetIt!.registerSingleton<SecureStorage>(secureStorage);
|
||||
|
||||
// 2. API Client
|
||||
_testGetIt!.registerSingleton<ApiClient>(
|
||||
ApiClient(dio),
|
||||
ApiClient(),
|
||||
);
|
||||
|
||||
// 3. Remote DataSources
|
||||
_testGetIt!.registerLazySingleton<AuthRemoteDataSource>(
|
||||
() => AuthRemoteDataSource(_testGetIt!<ApiClient>()),
|
||||
() => AuthRemoteDataSourceImpl(_testGetIt!<ApiClient>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<CompanyRemoteDataSource>(
|
||||
() => CompanyRemoteDataSource(_testGetIt!<ApiClient>()),
|
||||
() => CompanyRemoteDataSourceImpl(_testGetIt!<ApiClient>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<EquipmentRemoteDataSource>(
|
||||
() => EquipmentRemoteDataSource(_testGetIt!<ApiClient>()),
|
||||
() => EquipmentRemoteDataSourceImpl(),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<LicenseRemoteDataSource>(
|
||||
() => LicenseRemoteDataSource(_testGetIt!<ApiClient>()),
|
||||
() => LicenseRemoteDataSourceImpl(apiClient: _testGetIt!<ApiClient>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<UserRemoteDataSource>(
|
||||
() => UserRemoteDataSource(_testGetIt!<ApiClient>()),
|
||||
() => UserRemoteDataSourceImpl(_testGetIt!<ApiClient>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<WarehouseLocationRemoteDataSource>(
|
||||
() => WarehouseLocationRemoteDataSource(_testGetIt!<ApiClient>()),
|
||||
() => WarehouseLocationRemoteDataSourceImpl(apiClient: _testGetIt!<ApiClient>()),
|
||||
);
|
||||
|
||||
// 4. Repositories
|
||||
_testGetIt!.registerLazySingleton<AuthRepository>(
|
||||
() => AuthRepositoryImpl(
|
||||
remoteDataSource: _testGetIt!<AuthRemoteDataSource>(),
|
||||
prefs: _testGetIt!<SharedPreferences>(),
|
||||
sharedPreferences: _testGetIt!<SharedPreferences>(),
|
||||
),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<CompanyRepository>(
|
||||
@@ -153,7 +159,7 @@ class RealApiTestHelper {
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<EquipmentRepository>(
|
||||
() => EquipmentRepositoryImpl(
|
||||
remoteDataSource: _testGetIt!<EquipmentRemoteDataSource>(),
|
||||
_testGetIt!<EquipmentRemoteDataSource>(),
|
||||
),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<LicenseRepository>(
|
||||
@@ -172,120 +178,108 @@ class RealApiTestHelper {
|
||||
),
|
||||
);
|
||||
|
||||
// 5. UseCases - Auth
|
||||
// 6. UseCases - Auth
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => LoginUseCase(repository: _testGetIt!<AuthRepository>()),
|
||||
() => LoginUseCase(_testGetIt!<AuthService>()),
|
||||
);
|
||||
|
||||
// 6. UseCases - Company
|
||||
// 7. UseCases - Company
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => GetCompaniesUseCase(repository: _testGetIt!<CompanyRepository>()),
|
||||
() => GetCompaniesUseCase(_testGetIt!<CompanyService>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => GetCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
|
||||
() => GetCompanyDetailUseCase(_testGetIt!<CompanyService>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => CreateCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
|
||||
() => CreateCompanyUseCase(_testGetIt!<CompanyService>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => UpdateCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
|
||||
() => UpdateCompanyUseCase(_testGetIt!<CompanyService>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => DeleteCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
|
||||
() => DeleteCompanyUseCase(_testGetIt!<CompanyService>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => ToggleCompanyStatusUseCase(repository: _testGetIt!<CompanyRepository>()),
|
||||
() => ToggleCompanyStatusUseCase(_testGetIt!<CompanyService>()),
|
||||
);
|
||||
|
||||
// 7. UseCases - Equipment
|
||||
// 8. UseCases - Equipment
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => GetEquipmentsUseCase(repository: _testGetIt!<EquipmentRepository>()),
|
||||
() => GetEquipmentsUseCase(_testGetIt!<EquipmentService>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => GetEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
|
||||
() => EquipmentInUseCase(_testGetIt!<EquipmentService>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => CreateEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => UpdateEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => DeleteEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => EquipmentInUseCase(repository: _testGetIt!<EquipmentRepository>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => EquipmentOutUseCase(repository: _testGetIt!<EquipmentRepository>()),
|
||||
() => EquipmentOutUseCase(_testGetIt!<EquipmentService>()),
|
||||
);
|
||||
|
||||
// 8. UseCases - License
|
||||
// 9. UseCases - License
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => GetLicensesUseCase(repository: _testGetIt!<LicenseRepository>()),
|
||||
() => GetLicensesUseCase(_testGetIt!<LicenseRepository>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => GetLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
|
||||
() => GetLicenseDetailUseCase(_testGetIt!<LicenseRepository>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => CreateLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
|
||||
() => CreateLicenseUseCase(_testGetIt!<LicenseRepository>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => UpdateLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
|
||||
() => UpdateLicenseUseCase(_testGetIt!<LicenseRepository>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => DeleteLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
|
||||
() => DeleteLicenseUseCase(_testGetIt!<LicenseRepository>()),
|
||||
);
|
||||
|
||||
// 9. UseCases - User
|
||||
// 10. UseCases - User
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => GetUsersUseCase(repository: _testGetIt!<UserRepository>()),
|
||||
() => GetUsersUseCase(_testGetIt!<UserService>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => GetUserUseCase(repository: _testGetIt!<UserRepository>()),
|
||||
() => GetUserDetailUseCase(_testGetIt!<UserService>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => CreateUserUseCase(repository: _testGetIt!<UserRepository>()),
|
||||
() => CreateUserUseCase(_testGetIt!<UserService>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => UpdateUserUseCase(repository: _testGetIt!<UserRepository>()),
|
||||
() => UpdateUserUseCase(_testGetIt!<UserService>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => DeleteUserUseCase(repository: _testGetIt!<UserRepository>()),
|
||||
() => DeleteUserUseCase(_testGetIt!<UserService>()),
|
||||
);
|
||||
|
||||
// 10. UseCases - Warehouse Location
|
||||
// 11. UseCases - Warehouse Location
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => GetWarehouseLocationsUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
|
||||
() => GetWarehouseLocationsUseCase(_testGetIt!<WarehouseLocationRepository>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => GetWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
|
||||
() => GetWarehouseLocationDetailUseCase(_testGetIt!<WarehouseLocationRepository>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => CreateWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
|
||||
() => CreateWarehouseLocationUseCase(_testGetIt!<WarehouseLocationRepository>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => UpdateWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
|
||||
() => UpdateWarehouseLocationUseCase(_testGetIt!<WarehouseLocationRepository>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton(
|
||||
() => DeleteWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
|
||||
() => DeleteWarehouseLocationUseCase(_testGetIt!<WarehouseLocationRepository>()),
|
||||
);
|
||||
|
||||
// 11. Services (Legacy - 점진적 마이그레이션을 위해 유지)
|
||||
// 5. Services (현재 UseCases에서 사용 중)
|
||||
_testGetIt!.registerLazySingleton<AuthService>(
|
||||
() => AuthService(),
|
||||
() => AuthServiceImpl(_testGetIt!<AuthRemoteDataSource>(), _testGetIt!<FlutterSecureStorage>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<CompanyService>(
|
||||
() => CompanyService(),
|
||||
() => CompanyService(_testGetIt!<CompanyRemoteDataSource>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<EquipmentService>(
|
||||
() => EquipmentService(),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<LicenseService>(
|
||||
() => LicenseService(),
|
||||
() => LicenseService(_testGetIt!<LicenseRemoteDataSource>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<UserService>(
|
||||
() => UserService(),
|
||||
() => UserService(_testGetIt!<UserRemoteDataSource>()),
|
||||
);
|
||||
_testGetIt!.registerLazySingleton<WarehouseService>(
|
||||
() => WarehouseService(),
|
||||
@@ -303,9 +297,11 @@ class RealApiTestHelper {
|
||||
final getIt = await setupTestEnvironment();
|
||||
final loginUseCase = getIt<LoginUseCase>();
|
||||
|
||||
final result = await loginUseCase.execute(
|
||||
email: testEmail,
|
||||
password: testPassword,
|
||||
final result = await loginUseCase.call(
|
||||
LoginParams(
|
||||
email: testEmail,
|
||||
password: testPassword,
|
||||
),
|
||||
);
|
||||
|
||||
return result.fold(
|
||||
|
||||
@@ -366,7 +366,7 @@ class CompositeAction extends BaseTestableAction {
|
||||
continue;
|
||||
}
|
||||
|
||||
final result = await action.execute(tester);
|
||||
final result = await action.call(tester);
|
||||
results.add(result);
|
||||
|
||||
if (!result.success && stopOnFailure) {
|
||||
@@ -430,7 +430,7 @@ class ConditionalAction extends BaseTestableAction {
|
||||
final conditionMet = await condition(tester);
|
||||
|
||||
if (conditionMet) {
|
||||
final result = await trueAction.execute(tester);
|
||||
final result = await trueAction.call(tester);
|
||||
return ActionResult(
|
||||
success: result.success,
|
||||
message: 'Condition met - ${result.message}',
|
||||
@@ -441,7 +441,7 @@ class ConditionalAction extends BaseTestableAction {
|
||||
stackTrace: result.stackTrace,
|
||||
);
|
||||
} else if (falseAction != null) {
|
||||
final result = await falseAction!.execute(tester);
|
||||
final result = await falseAction!.call(tester);
|
||||
return ActionResult(
|
||||
success: result.success,
|
||||
message: 'Condition not met - ${result.message}',
|
||||
@@ -497,7 +497,7 @@ class RetryAction extends BaseTestableAction {
|
||||
continue;
|
||||
}
|
||||
|
||||
lastResult = await action.execute(tester);
|
||||
lastResult = await action.call(tester);
|
||||
|
||||
if (lastResult.success) {
|
||||
return ActionResult.success(
|
||||
|
||||
Reference in New Issue
Block a user