DEV Community

loading...
Cover image for Cleaner Flutter Vol. 8: Implementing contracts

Cleaner Flutter Vol. 8: Implementing contracts

marcossevilla profile image Marcos Sevilla ・3 min read

We have already talked about repositories, I would say quite a bit. But we still have one more part to cover about them, their implementation. At this point in the volumes of the series, we have established actions that we want to take through DataSources.

In general, implementing a repository is straightforward. Since they are only a class that manages the data sources and, through them, creates a complete data flow.

In the previous volume we saw about DataSources or DataProviders, so we are going to include them as dependencies of our repository.

class SomeRepository implements ISomeRepository {
    SomeRepository({
      required ILocalDataSource localDataSource,
      required IRemoteDataSource remoteDataSource,
    }) :  _localDataSource = localDataSource,
      _remoteDataSource = remoteDataSource;

    final ILocalDataSource _localDataSource;
    final IRemoteDataSource _remoteDataSource;
}
Enter fullscreen mode Exit fullscreen mode

Our repository also implements the interface (or abstract class) repository that we made from the domain layer, overriding its methods later on.

We can also see the dependency injection pattern that I like to follow, keeping variables private and only assigning it the value of a variable declared at the constructor scope.

This way of using private variables with dependency injection limits us to using this dependency indirectly and only through the object's methods, avoiding its direct use as a property (bypassing).

Implementing methods

We are going to override the getModel() method that sets our abstract repository to use our DataSources and define a specific data flow.

The getModel() method is in charge of fetching a specific data in a list based on an ID. This method uses the remoteDataSource to query the data to a REST API and from here the flow has two possible scenarios:

  1. If the API responds correctly, then the data is stored locally by the localDataSource.
  2. If the API had an error, then the last locally saved value is returned. In the event that there is no saved value, a cache error is thrown.

The code looks like this...

class SomeRepository implements ISomeRepository {
    SomeRepository({
      required ILocalDataSource localDataSource,
      required IRemoteDataSource remoteDataSource,
    }) :  _localDataSource = localDataSource,
      _remoteDataSource = remoteDataSource;

    final ILocalDataSource _localDataSource;
    final IRemoteDataSource _remoteDataSource;

    @override
    Future<SomeModel> getModel(int modelId) async {
      try {
        final model = await _remoteDataSource.getModel(modelId);
        await _localDataSource.saveModel(model);
        return model;
      } catch (e) {
        // si el llamado al API falla...
        try {
          final model = _localDataSource.getSavedModel();
          return model;
        } catch (e) {
          throw CacheError();
        }
      }
    }
}
Enter fullscreen mode Exit fullscreen mode

So we can see that our repository w as an intermediary for a concrete action. We are not interested in whether the data comes via HTTP or some other protocol, a DataSource takes care of that. Nor if it is saved in user preferences or some secure storage.

We are interested in the specific action of bringing an element, from which come different more steps of the process where several possible ways of interacting with this data converge.

Therefore, we leave interfaces or abstract classes of the DataSource so that they can be easily replaced by other implementations. That allows us to change a DataSource that occupies the shared_preferences package to one that occupies hive and does exactly the same thing. Thus the Liskov Substitution Principle is followed.

https://media.giphy.com/media/UuSDgyNq5Jqkkj0t5M/giphy.gif

As always...

You can share this article to help another developer to continue improving their productivity when writing applications with Flutter.

There's a Spanish version of this article on Medium. You're welcome. 🇪🇸

Also if you liked this content, you can find even more and keep in contact with me on my socials:

  • dev.to - where you're reading this article.
  • GitHub - where are my code repositories in case you like the examples.
  • LinkedIn - where I connect professionally.
  • Medium - where I publish my Spanish articles.
  • Twitter - where I express my short thoughts and share my content.
  • Twitch - where I do informal live shows from which I take clips with specific information.
  • YouTube - where I publish the clips that come out of my lives.

Discussion (1)

Collapse
pablonax profile image
Pablo Discobar

if you are interested in Flutter, then read this article - dev.to/pablonax/flutter-mobile-app...

Forem Open with the Forem app