Published in

ProAndroidDev

The “Real” Repository Pattern in Android

Over the years I’ve seen many implementations of the repository pattern, yet I think most of them are wrong and not beneficial.

These 5 are the most common mistakes I’ve seen (some of these are also in theofficial android documentation):

  1. The Repository returns a DTO instead of a Domain Model.
  2. DataSources (ApiServices, Daos..) use the same DTO.
  3. There is a Repository per set of endpoints and not per Entity (or Aggregate Root if you like DDD).
  4. The Repository caches the whole model, even those fields that need to be always up to date.
  5. A DataSource is used by more than one Repository.

That’s being said, how do we implement it right?

You need a Domain Model

This is a key point of the Repository Pattern and I believe developers struggle in doing it properly because they don’t understand what the Domain is.

By quotingMartin Fowlerwe can say that a Domain Model is:

An object model of the domain that incorporates both behavior and data.

Domain models basically represent Enterprise-wide business rules.

For who is not familiar withDomain-Driven Designbuilding blocks or withLayered architectures (Hexagonal, Onion, Clean…)there are 3 kinds of Domain Models:

  1. Entity: An entity is a plain object that has an identity (ID) and is potentially mutable.
  2. Value object: immutable object without identity.
  3. Aggregate root (DDD only): Entity that binds together with other entities (basically a cluster of associated objects).

In simple domains, these models will look very similar to the database and network models (DTOs), yet they still have lots of differences:

  • Domain Models mingle data and processes, and their structure is the most suitable for the app.
  • DTOs are the object model representation of a JSON/XML request/response or a database table, hence their structure is the most suitable for the remote communication.

Example of Domain Models:

Product.kt

Example of DTOs:

NetworkProduct.kt
DBProduct.kt

As you can see the Domain Model is free from frameworks and its structure promotes multivalued attributes (logically grouped as you can see inPrice) and uses theNull Object Pattern(fields are non-nullable), while DTOs are coupled with the framework (Gson, Room).

Thanks to this separation:

  • the development of our app becomes easier since we don’t have to bother in checking null values and the multivalued attributes don’t force us to send the whole model around.
  • changes in the data sources don’t affect our high-level policies.
  • there is more separation of concerns since we avoid “god models”
  • bad Backend implementations don’t affect our high-level policies (imagine if you are forced to perform 2 network requests because Backend is not able to give you all the information you need in a single one, would you let this issue affect your whole codebase?).

You need a Data Mapper

This is where we map DTOs into Domain Models and vice-versa.

Because most of the developers see this mapping as boring and unnecessary they prefer to couple their whole codebase, from the DataSources up to the UI, with DTOs.
While this makes (maybe) first releases faster to deliver, skipping the Domain Layer and coupling the UI with the DataSources produces several bugs that may be discovered only in production (i.e. BackEnd sending a null instead of an empty string which generates an NPE) other than hiding business rules and use cases in the presentation layer (i.e. Smart UI pattern).

In my opinion, mappers are fast to write and simple to test and even if their implementation is boring it makes sure we’ll never have surprises due to a change in the behavior of the DataSources.
If you don’t have time for mapping (or most probably don’t want to map) you can also rely on object mapping frameworks likehttp://modelmapper.org/for speeding up this process.

Because I don’t like to use frameworks in my usual implementation, in order to remove some boilerplate code, I have a generic mapper interface for avoiding to create an interface for each mapping:

Mapper.kt

And a set of generic ListMappers that avoid me to implement each specific List-to-List mapping:

ListMapper.kt
NullableInputListMapper.kt
NullableOutputListMapper

Edit:In this article, I show how to achieve the same with less boilerplate code using very basicFunctional Programming.

You need a different model for each DataSource

Let’s say we use a single model for both the network and the database:

ProductDTO.kt

At first, you may think this is way quicker than having 2 different models however do you see the risks of this approach?

If not I’ll list now a few for you:

  • You may cache more than needed.
  • Adding fields to the response will require Database migrations (unless you add an “@Ignore” annotation).
  • All the fields that we cache that we shouldn’t send as Request Body will need a “@Transient” annotation.
  • Unless we create new fields these must be of the same data type (we can’t, for example, parse anowPricestring from a network response and cache anowPricedouble).

As you can see the final result is that this approach requires way more maintenance than having separate models.

You should cache only what you need

Let’s say we want to show a list of products stored in a remote catalog and for each product, we want to show the classic heart icon when this is in ourlocalwishlist.

From this requirement, we understand that we need to:

  1. Fetch a list of products.
  2. Check our local storage to see if the products are in the local wishlist.

Our Domain Model will look like before with the addition of a field that says if the product is in the wishlist:

Our Network Model will look just like before and the Database Model, well is not needed.
For the local Wishlist, we can just store the products ids in theSharedPreferences.We don’t need to complicate the logic for doing something so simple and deal with Database migrations.

And finally our Repository:

ProductRepositoryImpl.kt

The dependencies used can be described as follows:

Components.kt

Now, what if we want to get only the products related to our wishlist?
In that case, the implementation would be very similar:

ProductRepositoryImpl.kt

Last notes

I’ve used this pattern many times at a professional level and I see it now as a lifesaver, especially in big projects.
However, I’ve seen many times developers using it “because they have to” and not because they know the real advantages of it.

I hope you found this article interesting and useful.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Baidu