I'm new to Flutter and have just heard the BLoC concept from reading tutorials about Flutter. From this tutorial, I heard BLoC for the first time. But I also see a file called "Repository" in this article. Basically the data flow goes like this:
Web API --> Api Provider --> Repository --> BLoC --> Widget
What I don't understand is that why there's a need for the Repository layer, as when I look at the repository file, it's basically just returning the API Provider's Future result? I got curious and try to search further, and I see some of the people's coding patterns on the internet also has a Repository layer on it.
In the original article, the API Provider does everything. It calls the get request, it awaits for the Future resolve, it converts the JSON data into appropriate model, and return the model enclosed with Future.
class ApiProvider {
Future<ItemModel> fetchMovieList() async {
final response = await client.get("http://api.themoviedb.org/3/movie/popular?api_key=$_apiKey");
if (response.statusCode == 200)
return ItemModel.fromJson(json.decode(response.body));
else
throw Exception('Failed to load post');
}
}
class Repository {
ApiProvider _api = ApiProvider();
Future<ItemModel> fetchMovieList() => _api.fetchMovieList(); // why?
}
class Bloc {
Repository _repository = Repository();
final _moviesFetcher = PublishSubject<ItemModel>();
Observable<ItemModel> get allMovies => _moviesFetcher.stream;
fetchAllMovies() async {
ItemModel itemModel = await
_repository.fetchAllMovies();
_moviesFetcher.sink.add(itemModel);
}
}
Currently I modify it so that the Api Provider returns pure Future, where the Repository implement the .then()
and convert the response into appropriate data, but I tend to avoid await because in React Native await causes the app to look unresponsive. I also move error checking into BLoC.
class ApiProvider {
Future fetchMovieList() => client.get("http://api.themoviedb.org/3/movie/popular?api_key=$_apiKey");
}
class Repository {
ApiProvider _api = ApiProvider();
Future<ItemModel> fetchMovieList() => _api.fetchMovieList().then(response => ItemModel.fromJson(json.decode(response.body));
}
class Bloc {
Repository _repository = Repository();
final _moviesFetcher = PublishSubject<ItemModel>();
Observable<ItemModel> get allMovies => _moviesFetcher.stream;
fetchAllMovies() async => _repository.fetchPopularMovies().then((response) => _moviesFetcher.sink.add(response))
.catchError((onError) => throw Exception("Failed to load post $onError"));
}
But still, I feel like this is a stretch to justify the need for this Repository layer. If I can, I want to make it like this:
class ApiProvider {
Future<ItemModel> fetchMovieList() => client.get("http://api.themoviedb.org/3/movie/popular?api_key=$_apiKey")
.then(response => ItemModel.fromJson(json.decode(response.body));
}
class Bloc {
ApiProvider _api = ApiProvider();
final _moviesFetcher = PublishSubject<ItemModel>();
Observable<ItemModel> get allMovies => _moviesFetcher.stream;
fetchAllMovies() async => _api.fetchPopularMovies().then((response) => _moviesFetcher.sink.add(response))
.catchError((onError) => throw Exception("Failed to load post $onError"));
}
and get rid of the Repository layer altogether. I'm not trying to say the Repository layer is unnecessary, but right now I don't know what pattern problem the Repository layer trying to solve. I just want to know why there's a Repository layer in the first place and what the real-world significant use case of Repository. I know this question may be flagged as a question that can trigger discussion instead of straight answers. But I believe there is some kind of narrowed answers for this question. I just can't find it when I tried to search on the internet (the search result got mixed up with other uses of "Repository" terms, like git and subversion).
Ok, forget about it. I found this excellent article that explains that basically Repository is to abstract where the data is coming from, whether it's from disk cache, cloud, or other source. The factory will decide what source to use based on the each source availability. The caller will just only need to go through one gate. Because the tutorial above has only one source (API/cloud), it looks useless to me at that moment.
Here is an excellent summary of the why. And it makes complete sense. This is from the BLoC documentation, where they detail a weather app tutorial that uses a Repository layer (see here for the full article).
"The goal of our repository layer is to abstract our data layer and facilitate communication with the bloc layer. In doing this, the rest of our code base depends only on functions exposed by our repository layer instead of specific data provider implementations. This allows us to change data providers without disrupting any of the application-level code. For example, if we decide to migrate away from metaweather, we should be able to create a new API client and swap it out without having to make changes to the public API of the repository or application layers."
I'm going for it!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With