avatarOlivier Revial

Summary

The provided content discusses the implementation of the data layer in a multi-package Clean Architecture for Flutter applications, detailing the flow from API calls to data conversion and caching strategies.

Abstract

The article is part of a series on implementing Clean Architecture in Flutter with separate packages. It builds upon the previous discussion of the domain layer by delving into the data layer, which is responsible for API calls, local storage, and caching strategies. The data layer's workflow is outlined, starting with the repository implementation that interacts with data sources and APIs, optionally caching the data, and finally converting it into domain entities. An example is given using the CoursesRepository from a demo app, illustrating the conversion of data models to domain entities. The article concludes by mentioning the upcoming topic of orchestrating the interaction between the different layers of the application.

Opinions

  • The author emphasizes the importance of separating the data layer from other layers, such as the domain layer, to maintain a clean architecture.
  • The data layer is described as potentially vast and varied, suggesting that it requires careful consideration and design.
  • The optional caching step indicates that caching strategies are flexible and can be adapted based on the application's needs.
  • The conversion of data models to domain entities is highlighted as a crucial step in the data layer's workflow, ensuring that the domain layer works with the correct level of abstraction.
  • The author provides a practical example from a demo app, which implies a preference for code examples to illustrate concepts.
  • The mention of the next chapter on orchestrating the app suggests that the integration of different layers is a non-trivial task that warrants its own discussion.

Multi-packages Clean Architecture in Flutter — Data layer

Photo by benjamin lehman on Unsplash

This article is part of a series on implementing a Clean Architecture with separated packages.

In the previous article, we’ve seen the domain layer that defines repositories interfaces. Let’s see how to implement them!

The data layer can be pretty vast as it can contains very various implementation details. Here are a few examples:

  • API calls
  • Local storage (database, file, shared preferences, etc)
  • Caching strategy

I won’t go too much into the details on each file in this layer because it is pretty straightforward but I will quickly the call flow of the data layer:

  1. The entrypoint of the data repository is the repository implementation (remember, the interface lives in the domain layer)
  2. A repository method will usually call an API or a datasource to get requested data, doing any deserialization operations that might be needed. At this point what we call the “data” is a model defined in the data layer, but it’s not yet the entity asked by the repository interface.
  3. [Optional] Depending on the strategy the repository method can choose to cache fetched data at this point
  4. The data model is converted to a domain entity and returned to the domain.

Here is an example of what I’ve just described with the implementation of the CoursesRepository from the demo app:

@Injectable(as: CoursesRepository)
// 1️⃣
class CoursesRepositoryImpl implements CoursesRepository {
  final UserDataSource userDataSource;
  final CoursesApi api;

  CoursesRepositoryImpl(this.userDataSource, this.api);

  @override
  Future<List<Course>> getCourses() {
    // 2️⃣
    return api
        .getCourses()
        // 3️⃣
        .then((courses) => courses.courses.map((e) => e.toEntity()).toList());
  }

  @override
  Future<List<CourseProgress>> getCoursesProgresses() {
    // 2️⃣
    return userDataSource
        .getCourseProgresses()
        // 3️⃣
        .then((progresses) => progresses.map((p) => p.toEntity()).toList());
  }

  @override
  Future<void> updateCourseProgress(CourseProgress progress) {
    // 2️⃣
    return userDataSource
        .updateCourseProgress(CourseProgressResponse.fromEntity(progress));
  }
}

And that’s pretty much all I wanted to show on the data layer!

🧑‍💻 If you want to explore the data layer further, have a look at the demo repository.

🧐 What’s next ?

We now have seen all the layers: presentation, domain and data. Phew 😮‍💨

There is however one topic left: how do all those layers work together, know about one another, etc. In the next and final chapter, we will see how to orchestrate our app.

Flutter
Clean Architecture
Recommended from ReadMedium