Files
finger_print_app/lib/ARCHITECTURE.md
Mohammed Al-Samarraie ac8a769ff0 11111
2026-01-13 12:43:43 +03:00

5.6 KiB

هيكلية المشروع (Project Architecture)

هذا المشروع يتبع نمط Clean Architecture مع فصل واضح بين الطبقات.

الهيكلية العامة

lib/
├── core/                    # المكونات الأساسية المشتركة
│   ├── constants/          # الثوابت (مثل الأبعاد)
│   ├── di/                 # Dependency Injection (GetIt)
│   ├── enums/              # التعدادات
│   ├── error/              # معالجة الأخطاء (Exceptions & Failures)
│   ├── network/            # عميل API (ApiClient)
│   └── utils/              # الأدوات المساعدة
│
├── data/                    # طبقة البيانات
│   ├── datasources/        # مصادر البيانات (Remote & Local)
│   ├── dto/                # Data Transfer Objects (للاتصال مع API)
│   └── repositories/       # تطبيقات الـ Repositories
│
├── domain/                  # طبقة الأعمال (Business Logic)
│   ├── models/             # نماذج الأعمال
│   ├── repositories/       # واجهات الـ Repositories
│   └── usecases/           # حالات الاستخدام (Use Cases)
│
├── presentation/            # طبقة العرض
│   ├── blocs/              # State Management (BLoC)
│   ├── screens/            # الشاشات
│   └── widgets/            # الويدجتات القابلة لإعادة الاستخدام
│
├── models/                  # النماذج القديمة (يمكن نقلها لـ domain/models)
├── screens/                 # الشاشات القديمة (يمكن نقلها لـ presentation/screens)
├── services/                # الخدمات القديمة
└── widgets/                 # الويدجتات القديمة (يمكن نقلها لـ presentation/widgets)

كيفية الاستخدام

1. إضافة API جديد

أ) إنشاء DTO في data/dto/

class LoginDto {
  final String phoneNumber;
  final String password;
  
  LoginDto({required this.phoneNumber, required this.password});
  
  Map<String, dynamic> toJson() => {
    'phoneNumber': phoneNumber,
    'password': password,
  };
}

ب) إنشاء Remote Data Source في data/datasources/

abstract class AuthRemoteDataSource {
  Future<LoginResponseDto> login(LoginDto dto);
}

class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
  final ApiClient apiClient;
  
  AuthRemoteDataSourceImpl({required this.apiClient});
  
  @override
  Future<LoginResponseDto> login(LoginDto dto) async {
    try {
      final response = await apiClient.post('/Auth/login', data: dto.toJson());
      // Handle response
    } on DioException catch (e) {
      // Handle errors
    }
  }
}

ج) إنشاء Repository Interface في domain/repositories/

abstract class AuthRepository {
  Future<Either<Failure, LoginResponseModel>> login(LoginRequest request);
}

د) إنشاء Repository Implementation في data/repositories/

class AuthRepositoryImpl implements AuthRepository {
  final AuthRemoteDataSource remoteDataSource;
  
  AuthRepositoryImpl({required this.remoteDataSource});
  
  @override
  Future<Either<Failure, LoginResponseModel>> login(LoginRequest request) async {
    try {
      final dto = LoginDto(...);
      final responseDto = await remoteDataSource.login(dto);
      final model = _convertDtoToModel(responseDto);
      return Right(model);
    } on ServerException catch (e) {
      return Left(ServerFailure(e.message));
    }
  }
}

هـ) إنشاء Use Case في domain/usecases/

class LoginUseCase {
  final AuthRepository repository;
  
  LoginUseCase({required this.repository});
  
  Future<Either<Failure, LoginResponseModel>> call(LoginRequest request) {
    return repository.login(request);
  }
}

و) تسجيل في core/di/injection_container.dart

// Data source
sl.registerLazySingleton<AuthRemoteDataSource>(
  () => AuthRemoteDataSourceImpl(apiClient: sl()),
);

// Repository
sl.registerLazySingleton<AuthRepository>(
  () => AuthRepositoryImpl(remoteDataSource: sl()),
);

// Use case
sl.registerLazySingleton(() => LoginUseCase(repository: sl()));

2. استخدام في BLoC

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  final LoginUseCase loginUseCase;
  
  LoginBloc({required this.loginUseCase}) : super(LoginInitial()) {
    on<LoginSubmitted>(_onLoginSubmitted);
  }
  
  Future<void> _onLoginSubmitted(
    LoginSubmitted event,
    Emitter<LoginState> emit,
  ) async {
    emit(LoginLoading());
    final result = await loginUseCase(event.request);
    result.fold(
      (failure) => emit(LoginError(failure.message)),
      (response) => emit(LoginSuccess(response)),
    );
  }
}

الحزم المستخدمة

  • dio: للاتصال بالـ API
  • get_it: لإدارة Dependency Injection
  • dartz: لاستخدام Either للتعامل مع الأخطاء
  • equatable: للمساواة بين الكائنات
  • shared_preferences: للتخزين المحلي

ملاحظات مهمة

  1. تحديث baseUrl: قم بتحديث baseUrl في core/network/api_client.dart
  2. إضافة Token: يمكن إضافة interceptor في ApiClient لإضافة token تلقائياً
  3. معالجة الأخطاء: جميع الأخطاء تمر عبر Exceptions ثم Failures
  4. التحويل: DTOs للـ API، Models للـ Domain