Either class
Domain Layer
Summary
This lesson introduces the functional programming concept of the Either type to explicitly handle success and failure states in the Domain Layer. Instead of relying on traditional try-catch blocks that allow exceptions to bubble up uncontrollably from the Data Layer through the architecture to the Presentation Layer, the Either class forces the caller to consciously handle both outcomes at compile time. By utilizing the dartz package, the repository contract is updated to return Either<Failure, List<BookEntity>>, ensuring a safer, more predictable data flow and cleaner control logic across architectural boundaries.
Key Ideas
- Relying purely on exceptions and try-catch for control flow can lead to unpredictable behavior as errors bubble up across architectural layers.
- Exceptions originating in the Data Layer (e.g., API or local database failures) should be caught early and converted into manageable error objects.
- Either<L, R> is a functional programming construct used to return one of two strictly typed possible values, preventing unhandled runtime exceptions.
- Left side (L) conventionally represents the failure or error state.
- Right side (R) conventionally represents the success state and the requested data (the "Right" is correct).
- dartz is a third-party package introduced to bring functional programming tools, including the Either class, to Dart.
- Failure acts as a custom domain object established in the core folder to represent application-specific errors cleanly.
Code Snippets
class Failure {}import 'package:bookly/core/errors/failure.dart';
import 'package:bookly/features/home/domain/entities/book_entity.dart';
import 'package:dartz/dartz.dart';
abstract class HomeRepo {
Future<Either<Failure, List<BookEntity>>> fetchFeaturedBooks();
Future<List<BookEntity>> fetchNewestBooks();
}Architecture Insight
Either<Failure, T> makes failure a first-class return value instead of a side-channel. The caller can't forget to handle it — the type system blocks them. That's the same compile-time discipline that makes Option<T> and Rust's Result work.
The instructor uses dartz. fpdart is the modern equivalent — null-safe, actively maintained, same Either API. Either is fine; both belong in the data and domain layers and never in widgets.
Looking ahead — Section 4's server-failure-part-1 builds out the Failure hierarchy this lesson sketches.
My Notes
- Control Flow vs. Exceptions: The core philosophy presented here is treating errors as standard data values rather than structural anomalies (exceptions). This heavily stabilizes the presentation layer, as UI state managers (like Bloc/Cubit) no longer need to wrap domain calls in try-catch blocks, they just map the Left/Right sides to UI states.
- [Editor's note] dartz vs fpdart: The instructor installs dartz. If you are starting a fresh project today, strongly consider fpdart instead. It provides the exact same Either<L, R> construct but is built for modern Dart (with better null-safety and documentation).
- [Editor's note] The Failure class: Right now, it is just an empty class. Moving forward, this should ideally be an abstract class that concrete failures (like ServerFailure or CacheFailure) extend, containing properties like a user-friendly errorMessage so the UI knows what to display.