arch-atlas

Implement remote fetch featured books

Data Layer

07:34

Summary

In this lesson, we implement the fetchFeaturedBooks method within the HomeRemoteDataSourceImpl. We inject the ApiService to handle the HTTP GET request to the Google Books API. The response data is parsed from JSON into a list of BookModel objects, which are safely returned as a list of BookEntity objects due to inheritance. Finally, we extract the JSON parsing logic into a separate helper method to adhere to the Single Responsibility Principle.

Key Ideas

  • ApiService is injected into the remote data source to handle HTTP requests abstractly.
  • Google Books API endpoint volumes?Filtering=free-ebooks&q=programming is used to fetch the featured books.
  • The returned JSON data contains a Map where the target book objects are nested under the items key.
  • BookModel objects are instantiated using the fromJson factory constructor inside a loop.
  • BookModel instances can be added directly to a List<BookEntity> because BookModel extends BookEntity.
  • The super constructor in BookModel maps the parsed JSON data directly to the required BookEntity fields.
  • Single Responsibility Principle (SRP) is applied by extracting the JSON-to-List parsing logic into a dedicated getBooksList helper method.

Code Snippets

Extracted helper method to parse the JSON response into a list of BookEntitiesdart
List<BookEntity> getBooksList(Map<String, dynamic> data) {
  List<BookEntity> books = [];
  for (var bookMap in data['items']) {
    books.add(BookModel.fromJson(bookMap));
  }
  return books;
}
HomeRemoteDataSourceImpl implementation with ApiService and fetchFeaturedBooksdart
class HomeRemoteDataSourceImpl extends HomeRemoteDataSource {
  final ApiService apiService;

  HomeRemoteDataSourceImpl(this.apiService);

  @override
  Future<List<BookEntity>> fetchFeaturedBooks() async {
    var data = await apiService.get(
        endPoint: 'volumes?Filtering=free-ebooks&q=programming');
    List<BookEntity> books = getBooksList(data);
    return books;
  }

  @override
  Future<List<BookEntity>> fetchNewestBooks() {
    // TODO: implement fetchNewestBooks
    throw UnimplementedError();
  }
}

My Notes

The instructor elegantly demonstrates polymorphism in Dart: returning a List<BookModel> where a List<BookEntity> is expected works seamlessly because BookModel extends BookEntity and calls super(). Refactoring the parsing logic out of fetchFeaturedBooks into getBooksList is a great call for the Single Responsibility Principle (SRP). [Editor's note] Placing getBooksList as a top-level function in the data source file might pollute the file over time. In a strict Clean Architecture setup, data mapping often lives inside the Model itself (e.g., BookModel.fromJsonList(...)) or inside a dedicated Mapper class to keep the Data Source purely focused on orchestration. [Editor's note] There is no error handling (like a try-catch block or checking if data['items'] is null) implemented yet. If the API returns an error or an empty response without the items key, the for...in loop will crash.