What is a Use Case in Clean Architecture and is it always necessary?
Short Answer
A Use Case represents a single, specific business action, sitting in the domain layer and orchestrating one or more repository calls; it's most valuable when business logic is genuinely complex, and can be overkill for simple CRUD-style operations.
A Use Case class typically exposes a single public method (often just call(), letting you invoke it like await loginUseCase(email, password)), containing business rules that don't belong in a widget or repository. It sits between presentation (which calls it) and data (repositories it calls), keeping business logic testable in complete isolation.
For an operation that's genuinely just 'call a repository method and return the result' with zero additional logic, wrapping it in a Use Case adds indirection without real benefit — some teams skip Use Cases for trivial passthrough operations.
Code Example
class LoginUseCase {
final AuthRepository _repo;
const LoginUseCase(this._repo);
Future<Either<Failure, User>> call({required String email, required String password}) {
if (email.isEmpty) return Future.value(Left(ValidationFailure('Email required')));
return _repo.login(email: email, password: password);
}
}Common Mistakes
- ×Mechanically creating a Use Case for every single repository method even when it adds zero logic.
- ×Putting complex business logic directly in a Bloc/ViewModel instead of a Use Case when it warrants isolated unit testing.