arch-atlas

Fetch featured books use case

Domain Layer

06:40

Summary

This lesson demonstrates the practical implementation of a UseCase in Clean Architecture by creating the FetchFeaturedBooksUseCase within the domain layer. The instructor explains that a UseCase represents a specific action a user can take, structured as an independent class rather than a simple method to ensure single responsibility and scalability. By abstracting the call to the Repository inside a dedicated UseCase, developers can cleanly inject future business logic, such as permission checks or validation, without cluttering the data layer or violating architectural principles.

Key Ideas

  • UseCases represent specific, individual actions that a user can trigger within a feature.
  • UseCases should be implemented as full classes rather than standalone methods to support dependency injection and maintain clean architecture boundaries.
  • The FetchFeaturedBooksUseCase class depends on the HomeRepo interface to fetch data, effectively hiding the implementation details of the data source.
  • Using a dedicated class for a single action strictly adheres to the Single Responsibility Principle (SRP) from the SOLID principles.
  • Abstracting calls into a UseCase allows for easy injection of pre-execution logic, such as permission checks, without modifying the repository interface or the presentation layer.
  • Separating logic into UseCases makes the codebase more maintainable, easier to test, and simpler to refactor as the application grows.

Code Snippets

Final state of fetch_featured_books_use_case.dartdart
import 'package:bookly/core/errors/failure.dart';
import 'package:bookly/features/home/domain/entities/book_entity.dart';
import 'package:bookly/features/home/domain/repos/home_repo.dart';
import 'package:dartz/dartz.dart';

class FetchFeaturedBooksUseCase {
  final HomeRepo homeRepo;

  FetchFeaturedBooksUseCase(this.homeRepo);

  Future<Either<Failure, List<BookEntity>>> fetchFeaturedBooks() {
    // check permission
    return homeRepo.fetchFeaturedBooks();
  }
}

My Notes

The instructor raises an excellent theoretical point here: why wrap a simple repository call inside a dedicated UseCase class if it's just passing the data through? The answer boils down to the Single Responsibility Principle (SRP) and future-proofing. If you skip the UseCase and call the repository directly from the presentation layer (e.g., from a Bloc or Cubit), you risk putting domain business logic—like permission checks or data formatting—inside your UI state management. [Editor's note] While the instructor named the method fetchFeaturedBooks(), standard Clean Architecture in Flutter often utilizes Dart's call() method. This allows you to invoke the UseCase instance like a function (e.g., fetchFeaturedBooksUseCase()), which makes the code slightly cleaner. Furthermore, a foundational UseCase abstract class or interface (often taking a generic Type and Params) is omitted in this video but is highly recommended for standardizing the input and output contracts of all UseCases in the app.