When developing good software, developers often rely on design patterns, which we can define as valuable and proven solutions to known problems. Arguably, the most quoted list of these patterns is the one compiled by the famous Gang of Four in their book “Design Patterns”, that explained, among several others, the Observer, the Adapter and the Builder patterns, just to name a few. Even though the Repository pattern has not been defined by this group of authors, it increasingly gained popularity from its first introduction, as it gives software developers a mediation layer between the domain of the application and the raw data fetched from the data source.
Repository pattern is a great idea, and it’s what Store is based on: instead of accessing the network layer directly, the library should be invoked, giving a wide range of benefits. One of them is the possibility of persisting the data so that it can be accessed while offline. This brings us to the next step, the record duration policy: within Store it’s possible to declare how long the data should persist. There is control over whether to fetch fresh data when what’s currently on disk is stale, or not. Store also provides total freedom on how to fetch, parse and persist data, while still giving good defaults.
In the Android community, the vast majority of developers use (or have used) Retrofit for network requests at least once, and it’s pretty common to have a service that can be similar to this (very basic) one:
This implementation is fairly straightforward, but what if the need for persistence arises? At this point, the Repository pattern can help quite a bit, and wrapping this service with Store is, in fact, just as easy.
For this example, RxJava and Moshi have been selected to be the reference, the first because Store relies heavily on it, while the latter is more of a personal choice: whether you use Moshi, Gson or Jackson, the steps to have a working Store instance are the same, with the sole difference of some prefixes.
Creating the Store
The main reason behind using Store is to have a simple and streamlined way to persist the data fetched from the source and this is what Store provides, but it also needs a buffered source in order to do so. Of course, one possibility would be to refactor the Retrofit service and have our calls return a
ResponseBody, but in most cases this is not feasible right away. Another option would be to convert result models back to buffered sources so that they can be persisted, and this is done by explicitly telling Store to use both the model returned by the Retrofit service and a
Persisting and parsing
As mentioned before, the model returned by the Retrofit service will be converted back to a
BufferedSource before it can be persisted, so the persister itself can be as easy as the basic example showed by the documentation:
The parsing step is what comes next, and it’s what can convert the
BufferedSource (previously stored with the
persister) back to the POJO and push it down the chain. This is also where Moshi comes into play, and a specific converter is needed: the
MoshiParserFactory is responsible for creating an object that can understand what arrives from the persistence layer and convert the raw data back to the expected model, the same model returned by the Retrofit service:
This is the most interesting part, that can also be the one giving the more headaches: the idea behind the fetcher is to transform the POJO into a stream of data, and for doing so, a specific
TransformerFactory and the RxJava
compose operator will be used. The idea is to get the call to the Retrofit service and transform the result before returning it. In this way, in case Store is not needed, the service has not been affected and can be used on its own:
Using the Repository
At this point, the only thing left to do is using the Store instance, that can be just this line:
and, of course, the same would be if the persistence layer was not needed (in which case the
fetch() method would be used):
Disclaimer: the process explained below is a first approach to integrate Store and Retrofit, but it is not suitable for production code. It’s a first step in that direction.
Implementing Store with an existing Retrofit instance is not hard at all, and it can take as little as 25 lines of code, without a single modification to the existing codebase.
This method is far from being the most optimised one: Retrofit is converting a
ResponseBody into a POJO; this POJO is then converted into a
BufferedSource for persistence, and back to the POJO. A much better way of doing it would be to refactor the Retrofit service so that it returns a
ResponseBody directly, but this step can be taken later on.