fill
This commit is contained in:
12
lib/features/auth/domain/domain.dart
Normal file
12
lib/features/auth/domain/domain.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
/// Barrel file for auth domain layer exports
|
||||
///
|
||||
/// Provides clean imports for domain layer components
|
||||
|
||||
// Entities
|
||||
export 'entities/user_entity.dart';
|
||||
|
||||
// Repositories
|
||||
export 'repositories/auth_repository.dart';
|
||||
|
||||
// Use cases
|
||||
export 'usecases/login_usecase.dart';
|
||||
48
lib/features/auth/domain/entities/user_entity.dart
Normal file
48
lib/features/auth/domain/entities/user_entity.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// User entity representing authenticated user in the domain layer
|
||||
///
|
||||
/// This is a pure domain model with no external dependencies
|
||||
class UserEntity extends Equatable {
|
||||
/// Unique user identifier
|
||||
final String userId;
|
||||
|
||||
/// Username
|
||||
final String username;
|
||||
|
||||
/// Access token for API authentication
|
||||
final String accessToken;
|
||||
|
||||
/// Refresh token for renewing access token
|
||||
final String? refreshToken;
|
||||
|
||||
const UserEntity({
|
||||
required this.userId,
|
||||
required this.username,
|
||||
required this.accessToken,
|
||||
this.refreshToken,
|
||||
});
|
||||
|
||||
/// Create a copy with modified fields
|
||||
UserEntity copyWith({
|
||||
String? userId,
|
||||
String? username,
|
||||
String? accessToken,
|
||||
String? refreshToken,
|
||||
}) {
|
||||
return UserEntity(
|
||||
userId: userId ?? this.userId,
|
||||
username: username ?? this.username,
|
||||
accessToken: accessToken ?? this.accessToken,
|
||||
refreshToken: refreshToken ?? this.refreshToken,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [userId, username, accessToken, refreshToken];
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UserEntity(userId: $userId, username: $username, hasRefreshToken: ${refreshToken != null})';
|
||||
}
|
||||
}
|
||||
46
lib/features/auth/domain/repositories/auth_repository.dart
Normal file
46
lib/features/auth/domain/repositories/auth_repository.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../../../../core/errors/failures.dart';
|
||||
import '../../data/models/login_request_model.dart';
|
||||
import '../entities/user_entity.dart';
|
||||
|
||||
/// Abstract repository interface for authentication operations
|
||||
///
|
||||
/// This defines the contract that the data layer must implement.
|
||||
/// Returns Either<Failure, Success> for proper error handling.
|
||||
abstract class AuthRepository {
|
||||
/// Login with username and password
|
||||
///
|
||||
/// Returns [Right(UserEntity)] on success
|
||||
/// Returns [Left(Failure)] on error
|
||||
Future<Either<Failure, UserEntity>> login(LoginRequestModel request);
|
||||
|
||||
/// Logout current user
|
||||
///
|
||||
/// Returns [Right(void)] on success
|
||||
/// Returns [Left(Failure)] on error
|
||||
Future<Either<Failure, void>> logout();
|
||||
|
||||
/// Refresh access token
|
||||
///
|
||||
/// Returns [Right(UserEntity)] with new tokens on success
|
||||
/// Returns [Left(Failure)] on error
|
||||
Future<Either<Failure, UserEntity>> refreshToken(String refreshToken);
|
||||
|
||||
/// Check if user is authenticated
|
||||
///
|
||||
/// Returns true if valid access token exists
|
||||
Future<bool> isAuthenticated();
|
||||
|
||||
/// Get current user from local storage
|
||||
///
|
||||
/// Returns [Right(UserEntity)] if user data exists
|
||||
/// Returns [Left(Failure)] if no user data found
|
||||
Future<Either<Failure, UserEntity>> getCurrentUser();
|
||||
|
||||
/// Clear authentication data (logout locally)
|
||||
///
|
||||
/// Returns [Right(void)] on success
|
||||
/// Returns [Left(Failure)] on error
|
||||
Future<Either<Failure, void>> clearAuthData();
|
||||
}
|
||||
126
lib/features/auth/domain/usecases/login_usecase.dart
Normal file
126
lib/features/auth/domain/usecases/login_usecase.dart
Normal file
@@ -0,0 +1,126 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../../../../core/errors/failures.dart';
|
||||
import '../../data/models/login_request_model.dart';
|
||||
import '../entities/user_entity.dart';
|
||||
import '../repositories/auth_repository.dart';
|
||||
|
||||
/// Use case for user login
|
||||
///
|
||||
/// Encapsulates the business logic for authentication
|
||||
/// Validates input, calls repository, and handles the response
|
||||
class LoginUseCase {
|
||||
final AuthRepository repository;
|
||||
|
||||
LoginUseCase(this.repository);
|
||||
|
||||
/// Execute login operation
|
||||
///
|
||||
/// [request] - Login credentials (username and password)
|
||||
///
|
||||
/// Returns [Right(UserEntity)] on successful login
|
||||
/// Returns [Left(Failure)] on error:
|
||||
/// - [ValidationFailure] if credentials are invalid
|
||||
/// - [AuthenticationFailure] if login fails
|
||||
/// - [NetworkFailure] if network error occurs
|
||||
Future<Either<Failure, UserEntity>> call(LoginRequestModel request) async {
|
||||
// Validate input
|
||||
final validationError = _validateInput(request);
|
||||
if (validationError != null) {
|
||||
return Left(validationError);
|
||||
}
|
||||
|
||||
// Call repository to perform login
|
||||
return await repository.login(request);
|
||||
}
|
||||
|
||||
/// Validate login request input
|
||||
///
|
||||
/// Returns [ValidationFailure] if validation fails, null otherwise
|
||||
ValidationFailure? _validateInput(LoginRequestModel request) {
|
||||
// Validate username
|
||||
if (request.username.trim().isEmpty) {
|
||||
return const ValidationFailure('Username is required');
|
||||
}
|
||||
|
||||
if (request.username.length < 3) {
|
||||
return const ValidationFailure('Username must be at least 3 characters');
|
||||
}
|
||||
|
||||
// Validate password
|
||||
if (request.password.isEmpty) {
|
||||
return const ValidationFailure('Password is required');
|
||||
}
|
||||
|
||||
if (request.password.length < 6) {
|
||||
return const ValidationFailure('Password must be at least 6 characters');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Use case for user logout
|
||||
class LogoutUseCase {
|
||||
final AuthRepository repository;
|
||||
|
||||
LogoutUseCase(this.repository);
|
||||
|
||||
/// Execute logout operation
|
||||
///
|
||||
/// Returns [Right(void)] on successful logout
|
||||
/// Returns [Left(Failure)] on error
|
||||
Future<Either<Failure, void>> call() async {
|
||||
return await repository.logout();
|
||||
}
|
||||
}
|
||||
|
||||
/// Use case for checking authentication status
|
||||
class CheckAuthStatusUseCase {
|
||||
final AuthRepository repository;
|
||||
|
||||
CheckAuthStatusUseCase(this.repository);
|
||||
|
||||
/// Check if user is authenticated
|
||||
///
|
||||
/// Returns true if user has valid access token
|
||||
Future<bool> call() async {
|
||||
return await repository.isAuthenticated();
|
||||
}
|
||||
}
|
||||
|
||||
/// Use case for getting current user
|
||||
class GetCurrentUserUseCase {
|
||||
final AuthRepository repository;
|
||||
|
||||
GetCurrentUserUseCase(this.repository);
|
||||
|
||||
/// Get current authenticated user
|
||||
///
|
||||
/// Returns [Right(UserEntity)] if user is authenticated
|
||||
/// Returns [Left(Failure)] if no user found or error occurs
|
||||
Future<Either<Failure, UserEntity>> call() async {
|
||||
return await repository.getCurrentUser();
|
||||
}
|
||||
}
|
||||
|
||||
/// Use case for refreshing access token
|
||||
class RefreshTokenUseCase {
|
||||
final AuthRepository repository;
|
||||
|
||||
RefreshTokenUseCase(this.repository);
|
||||
|
||||
/// Refresh access token using refresh token
|
||||
///
|
||||
/// [refreshToken] - The refresh token
|
||||
///
|
||||
/// Returns [Right(UserEntity)] with new tokens on success
|
||||
/// Returns [Left(Failure)] on error
|
||||
Future<Either<Failure, UserEntity>> call(String refreshToken) async {
|
||||
if (refreshToken.isEmpty) {
|
||||
return const Left(ValidationFailure('Refresh token is required'));
|
||||
}
|
||||
|
||||
return await repository.refreshToken(refreshToken);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user