Use Case Generation Command
flx gen usecase
কমান্ড Clean Architecture-এর Domain layer-এ use case তৈরি করে। এটি business logic এবং application-specific rules implement করে।
Basic Usage
flx gen usecase usecase_name
Advanced Usage
flx gen usecase usecase_name --feature=auth --type=complex --with-validation
Available Options
Option | Description | Values | Default |
---|---|---|---|
--feature | Target feature name | Any valid feature | Required |
--type | Use case complexity | simple , complex , stream | simple |
--with-validation | Include input validation | true , false | true |
--with-error-handling | Include error handling | true , false | true |
--return-type | Return type pattern | either , future , stream | either |
Examples
1. Simple CRUD Use Case
flx gen usecase get_user --feature=user --type=simple
Generated Structure:
lib/features/user/
└── domain/
└── usecases/
└── get_user_usecase.dart
Generated Code:
import 'package:dartz/dartz.dart';
import '../../../../core/error/failures.dart';
import '../../../../core/usecases/usecase.dart';
import '../entities/user_entity.dart';
import '../repositories/user_repository.dart';
class GetUserUsecase implements UseCase<UserEntity, GetUserParams> {
final UserRepository repository;
GetUserUsecase(this.repository);
Future<Either<Failure, UserEntity>> call(GetUserParams params) async {
try {
// Input validation
if (params.userId.isEmpty) {
return Left(ValidationFailure('User ID cannot be empty'));
}
// Repository call
final result = await repository.getUser(params.userId);
return result.fold(
(failure) => Left(failure),
(user) {
// Business logic validation
if (!_isValidUser(user)) {
return Left(BusinessLogicFailure('Invalid user data'));
}
return Right(user);
},
);
} catch (e) {
return Left(UnexpectedFailure('Unexpected error: ${e.toString()}'));
}
}
bool _isValidUser(UserEntity user) {
return user.email.isNotEmpty &&
user.name.isNotEmpty &&
user.status == UserStatus.active;
}
}
class GetUserParams extends Equatable {
final String userId;
final bool includeProfile;
const GetUserParams({
required this.userId,
this.includeProfile = false,
});
List<Object?> get props => [userId, includeProfile];
}
2. Complex Business Logic Use Case
flx gen usecase calculate_loan --feature=finance --type=complex --with-validation
Generated Code:
import 'package:dartz/dartz.dart';
import '../../../../core/error/failures.dart';
import '../../../../core/usecases/usecase.dart';
import '../entities/loan_entity.dart';
import '../entities/credit_score_entity.dart';
import '../repositories/finance_repository.dart';
class CalculateLoanUsecase implements UseCase<LoanEntity, CalculateLoanParams> {
final FinanceRepository repository;
CalculateLoanUsecase(this.repository);
Future<Either<Failure, LoanEntity>> call(CalculateLoanParams params) async {
try {
// Complex input validation
final validationResult = _validateLoanParameters(params);
if (validationResult.isLeft()) {
return validationResult.fold(
(failure) => Left(failure),
(_) => throw UnimplementedError(),
);
}
// Get credit score
final creditScoreResult = await repository.getCreditScore(params.applicantId);
if (creditScoreResult.isLeft()) {
return Left(BusinessLogicFailure('Unable to retrieve credit score'));
}
final creditScore = creditScoreResult.getOrElse(() => throw UnimplementedError());
// Complex business logic
final loanCalculation = _performLoanCalculation(params, creditScore);
if (loanCalculation.isLeft()) {
return loanCalculation;
}
final loan = loanCalculation.getOrElse(() => throw UnimplementedError());
// Risk assessment
final riskAssessment = await _performRiskAssessment(loan, creditScore);
if (riskAssessment.isLeft()) {
return riskAssessment;
}
// Save calculation result
final saveResult = await repository.saveLoanCalculation(loan);
return saveResult.fold(
(failure) => Left(failure),
(_) => Right(loan),
);
} catch (e) {
return Left(UnexpectedFailure('Calculation error: ${e.toString()}'));
}
}
Either<Failure, void> _validateLoanParameters(CalculateLoanParams params) {
// Amount validation
if (params.amount <= 0) {
return Left(ValidationFailure('Loan amount must be positive'));
}
if (params.amount > 10000000) {
return Left(ValidationFailure('Loan amount exceeds maximum limit'));
}
// Term validation
if (params.termInMonths < 12 || params.termInMonths > 360) {
return Left(ValidationFailure('Loan term must be between 12-360 months'));
}
// Interest rate validation
if (params.interestRate < 0.01 || params.interestRate > 0.50) {
return Left(ValidationFailure('Invalid interest rate'));
}
// Applicant validation
if (params.applicantId.isEmpty) {
return Left(ValidationFailure('Applicant ID is required'));
}
return const Right(null);
}
Either<Failure, LoanEntity> _performLoanCalculation(
CalculateLoanParams params,
CreditScoreEntity creditScore,
) {
try {
// Interest rate adjustment based on credit score
double adjustedRate = _calculateAdjustedInterestRate(
params.interestRate,
creditScore.score,
);
// Monthly payment calculation
double monthlyPayment = _calculateMonthlyPayment(
params.amount,
adjustedRate,
params.termInMonths,
);
// Total payment calculation
double totalPayment = monthlyPayment * params.termInMonths;
double totalInterest = totalPayment - params.amount;
// Debt-to-income ratio check
if (params.monthlyIncome > 0) {
double debtToIncomeRatio = monthlyPayment / params.monthlyIncome;
if (debtToIncomeRatio > 0.43) {
return Left(BusinessLogicFailure(
'Debt-to-income ratio too high: ${(debtToIncomeRatio * 100).toStringAsFixed(1)}%'
));
}
}
final loan = LoanEntity(
id: DateTime.now().millisecondsSinceEpoch.toString(),
applicantId: params.applicantId,
amount: params.amount,
interestRate: adjustedRate,
termInMonths: params.termInMonths,
monthlyPayment: monthlyPayment,
totalPayment: totalPayment,
totalInterest: totalInterest,
status: LoanStatus.calculated,
calculatedAt: DateTime.now(),
creditScore: creditScore.score,
);
return Right(loan);
} catch (e) {
return Left(CalculationFailure('Loan calculation failed: ${e.toString()}'));
}
}
double _calculateAdjustedInterestRate(double baseRate, int creditScore) {
if (creditScore >= 750) return baseRate * 0.9; // 10% discount
if (creditScore >= 700) return baseRate * 0.95; // 5% discount
if (creditScore >= 650) return baseRate; // No change
if (creditScore >= 600) return baseRate * 1.1; // 10% increase
return baseRate * 1.25; // 25% increase for poor credit
}
double _calculateMonthlyPayment(double principal, double annualRate, int months) {
double monthlyRate = annualRate / 12;
double numerator = principal * monthlyRate * pow(1 + monthlyRate, months);
double denominator = pow(1 + monthlyRate, months) - 1;
return numerator / denominator;
}
Future<Either<Failure, void>> _performRiskAssessment(
LoanEntity loan,
CreditScoreEntity creditScore,
) async {
try {
// Risk factors calculation
double riskScore = 0.0;
// Credit score risk (40% weight)
riskScore += _calculateCreditRisk(creditScore.score) * 0.4;
// Loan amount risk (30% weight)
riskScore += _calculateAmountRisk(loan.amount) * 0.3;
// Term risk (20% weight)
riskScore += _calculateTermRisk(loan.termInMonths) * 0.2;
// Payment-to-income risk (10% weight)
if (loan.monthlyIncome > 0) {
riskScore += _calculateIncomeRisk(
loan.monthlyPayment / loan.monthlyIncome
) * 0.1;
}
// Risk threshold check
if (riskScore > 0.8) {
return Left(BusinessLogicFailure(
'Loan application rejected due to high risk score: ${(riskScore * 100).toStringAsFixed(1)}%'
));
}
// Update loan with risk assessment
loan.riskScore = riskScore;
return const Right(null);
} catch (e) {
return Left(RiskAssessmentFailure('Risk assessment failed: ${e.toString()}'));
}
}
double _calculateCreditRisk(int creditScore) {
if (creditScore >= 750) return 0.1;
if (creditScore >= 700) return 0.3;
if (creditScore >= 650) return 0.5;
if (creditScore >= 600) return 0.7;
return 0.9;
}
double _calculateAmountRisk(double amount) {
if (amount <= 100000) return 0.1;
if (amount <= 500000) return 0.3;
if (amount <= 1000000) return 0.5;
if (amount <= 5000000) return 0.7;
return 0.9;
}
double _calculateTermRisk(int termMonths) {
if (termMonths <= 60) return 0.1;
if (termMonths <= 120) return 0.3;
if (termMonths <= 240) return 0.5;
if (termMonths <= 300) return 0.7;
return 0.9;
}
double _calculateIncomeRisk(double debtToIncomeRatio) {
if (debtToIncomeRatio <= 0.2) return 0.1;
if (debtToIncomeRatio <= 0.3) return 0.3;
if (debtToIncomeRatio <= 0.35) return 0.5;
if (debtToIncomeRatio <= 0.4) return 0.7;
return 0.9;
}
}
class CalculateLoanParams extends Equatable {
final String applicantId;
final double amount;
final double interestRate;
final int termInMonths;
final double monthlyIncome;
final String loanType;
const CalculateLoanParams({
required this.applicantId,
required this.amount,
required this.interestRate,
required this.termInMonths,
required this.monthlyIncome,
this.loanType = 'personal',
});
List<Object?> get props => [
applicantId,
amount,
interestRate,
termInMonths,
monthlyIncome,
loanType,
];
}
3. Stream-based Use Case
flx gen usecase watch_notifications --feature=notification --type=stream
Generated Code:
import 'package:dartz/dartz.dart';
import '../../../../core/error/failures.dart';
import '../../../../core/usecases/stream_usecase.dart';
import '../entities/notification_entity.dart';
import '../repositories/notification_repository.dart';
class WatchNotificationsUsecase implements StreamUseCase<List<NotificationEntity>, WatchNotificationsParams> {
final NotificationRepository repository;
WatchNotificationsUsecase(this.repository);
Stream<Either<Failure, List<NotificationEntity>>> call(WatchNotificationsParams params) {
try {
// Validation
if (params.userId.isEmpty) {
return Stream.value(Left(ValidationFailure('User ID cannot be empty')));
}
// Get notification stream from repository
return repository.watchNotifications(params.userId).map((result) {
return result.fold(
(failure) => Left(failure),
(notifications) {
// Filter notifications based on parameters
final filteredNotifications = _filterNotifications(notifications, params);
// Sort notifications
final sortedNotifications = _sortNotifications(filteredNotifications, params.sortBy);
return Right(sortedNotifications);
},
);
}).handleError((error) {
return Left(UnexpectedFailure('Stream error: ${error.toString()}'));
});
} catch (e) {
return Stream.value(Left(UnexpectedFailure('Usecase error: ${e.toString()}')));
}
}
List<NotificationEntity> _filterNotifications(
List<NotificationEntity> notifications,
WatchNotificationsParams params,
) {
var filtered = notifications;
// Filter by read status
if (params.unreadOnly) {
filtered = filtered.where((n) => !n.isRead).toList();
}
// Filter by type
if (params.types.isNotEmpty) {
filtered = filtered.where((n) => params.types.contains(n.type)).toList();
}
// Filter by priority
if (params.minPriority != null) {
filtered = filtered.where((n) => n.priority.index >= params.minPriority!.index).toList();
}
// Filter by date range
if (params.fromDate != null) {
filtered = filtered.where((n) => n.createdAt.isAfter(params.fromDate!)).toList();
}
if (params.toDate != null) {
filtered = filtered.where((n) => n.createdAt.isBefore(params.toDate!)).toList();
}
return filtered;
}
List<NotificationEntity> _sortNotifications(
List<NotificationEntity> notifications,
NotificationSortBy sortBy,
) {
switch (sortBy) {
case NotificationSortBy.date:
notifications.sort((a, b) => b.createdAt.compareTo(a.createdAt));
break;
case NotificationSortBy.priority:
notifications.sort((a, b) => b.priority.index.compareTo(a.priority.index));
break;
case NotificationSortBy.type:
notifications.sort((a, b) => a.type.compareTo(b.type));
break;
}
return notifications;
}
}
class WatchNotificationsParams extends Equatable {
final String userId;
final bool unreadOnly;
final List<String> types;
final NotificationPriority? minPriority;
final DateTime? fromDate;
final DateTime? toDate;
final NotificationSortBy sortBy;
const WatchNotificationsParams({
required this.userId,
this.unreadOnly = false,
this.types = const [],
this.minPriority,
this.fromDate,
this.toDate,
this.sortBy = NotificationSortBy.date,
});
List<Object?> get props => [
userId,
unreadOnly,
types,
minPriority,
fromDate,
toDate,
sortBy,
];
}
enum NotificationSortBy { date, priority, type }
Use Case Patterns
1. Command Pattern (CUD Operations)
class CreateUserUsecase implements UseCase<UserEntity, CreateUserParams> {
// Implementation for creating resources
}
2. Query Pattern (Read Operations)
class GetUsersUsecase implements UseCase<List<UserEntity>, GetUsersParams> {
// Implementation for reading resources
}
3. Stream Pattern (Real-time Operations)
class WatchUserStatusUsecase implements StreamUseCase<UserStatus, WatchUserStatusParams> {
// Implementation for streaming data
}
Best Practices
- Single Responsibility: প্রতিটি use case একটি specific business operation handle করবে
- Input Validation: সব input parameters validate করুন
- Error Handling: Comprehensive error handling implement করুন
- Business Rules: Domain-specific rules enforce করুন
- Testing: Unit tests লিখুন প্রতিটি use case-এর জন্য
Dependencies
Use case generation-এর জন্য এই packages প্রয়োজন:
dependencies:
dartz: ^0.10.1
equatable: ^2.0.5
dev_dependencies:
mockito: ^5.4.2
test: ^1.24.6
Troubleshooting
Common Issues
- Repository Not Found: Feature-এর repository আগে তৈরি করুন
- Entity Missing: Domain entity আগে define করুন
- Import Errors: Proper package structure maintain করুন
এই command ব্যবহার করে আপনি scalable এবং testable business logic তৈরি করতে পারবেন!