Bidev
IntermediateArchitecture

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.