স্কিপ করে মূল কন্টেন্ট এ যান

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

OptionDescriptionValuesDefault
--featureTarget feature nameAny valid featureRequired
--typeUse case complexitysimple, complex, streamsimple
--with-validationInclude input validationtrue, falsetrue
--with-error-handlingInclude error handlingtrue, falsetrue
--return-typeReturn type patterneither, future, streameither

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

  1. Single Responsibility: প্রতিটি use case একটি specific business operation handle করবে
  2. Input Validation: সব input parameters validate করুন
  3. Error Handling: Comprehensive error handling implement করুন
  4. Business Rules: Domain-specific rules enforce করুন
  5. 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

  1. Repository Not Found: Feature-এর repository আগে তৈরি করুন
  2. Entity Missing: Domain entity আগে define করুন
  3. Import Errors: Proper package structure maintain করুন

এই command ব্যবহার করে আপনি scalable এবং testable business logic তৈরি করতে পারবেন!