Home remote data source
Data Layer
Summary
This lesson introduces the data sources layer within the Clean Architecture data layer, specifically focusing on building the remote data source for the home feature. The instructor emphasizes the importance of abstraction by defining an abstract HomeRemoteDataSource class before creating its concrete implementation. A critical architectural boundary is established: unlike the domain repository, the remote data source methods do not return an Either type. Its sole responsibility is to fetch and return the data (as entities) or let an exception bubble up, firmly delegating the responsibility of exception catching and failure handling to the repository layer.
Key Ideas
- The data layer is structurally divided into models and data_sources (which further splits into remote and local).
- Directly implementing data-fetching logic without a contract violates Clean Architecture principles; an abstract class must be defined first.
- HomeRemoteDataSource serves as the abstract contract outlining operations like fetchFeaturedBooks and fetchNewestBooks.
- Remote data source methods return Future<List<BookEntity>> rather than Future<Either<Failure, List<BookEntity>>>.
- Error handling, catching exceptions, and returning the Either type are strict responsibilities of the Repository layer, not the Data Source.
- HomeRemoteDataSourceImpl extends the abstract contract to provide the actual API or database implementation.
- Abstraction at the data source level enables parallel team development and simplifies future migrations (e.g., swapping a REST API for Firebase).
Code Snippets
import '../../domain/entities/book_entity.dart';
abstract class HomeRemoteDataSource {
Future<List<BookEntity>> fetchFeaturedBooks();
Future<List<BookEntity>> fetchNewestBooks();
}
class HomeRemoteDataSourceImpl extends HomeRemoteDataSource {
@override
Future<List<BookEntity>> fetchFeaturedBooks() {
// TODO: implement fetchFeaturedBooks
throw UnimplementedError();
}
@override
Future<List<BookEntity>> fetchNewestBooks() {
// TODO: implement fetchNewestBooks
throw UnimplementedError();
}
}Architecture Insight
Remote data sources return models, not entities. The model is a faithful representation of what the API actually sent; the entity is what the rest of the app wants to consume. Doing the model→entity step inside the data source (or the repo) is what keeps the seam clean.
If the data source returned List<BookEntity> directly, you'd be doing JSON decoding inside an "entity factory," and any test that wanted to mock the data source would need to know the JSON shape. Returning models keeps that knowledge contained.
My Notes
The instructor highlights a crucial boundary in Clean Architecture: Data Sources vs. Repositories. The HomeRemoteDataSource deliberately drops the Either<Failure, T> return type that was used in the HomeRepo. The data source acts as a straightforward conduit—it either successfully retrieves the data or throws an exception. Catching that exception and securely mapping it to a Failure object is delegated entirely to the repository implementation (HomeRepoImpl).
- [Editor's note] Entity vs. Model leakage: Returning BookEntity directly from the Data Source bypasses the Data Model mapping phase typically seen in strict Clean Architecture. Usually, Data Sources deal exclusively with raw JSON and Data Models, leaving the Repository to cast or map those Models into Domain Entities. While this shortcut reduces boilerplate and is common in pragmatic Flutter development, it tightly couples the external data structure to the core domain.
- Team workflow: This abstraction allows for parallel work streams; UI developers can use a mocked HomeRemoteDataSource while backend integrators write the actual HTTP calls.