Unfortunately, there are a lot of us who have chosen Parse in the past to implement their Backend-as-a-Service, especially because of its easy to use API. However, now, we all have to start moving away from it and reflect on two points:
- Why swapping backend has become a problem for our applications?
- What alternative BaaS can we use?
Why swapping backend has become a problem for our applications?
We all talks about Clean Architecture, MVC, MVP, MVVM etc.., but then a lot of people, including myself, often end up in having high-coupled applications. Why???
Personally, I have noticed that, whenever we need to use some new API’s, we tend to look at the examples provided by Google or by the API provider in turn. Those examples, in order to be concise and easily understandable, show code which mix together a lot of different responsibilities, UI management, Activity lifecycle, business logic, API calls and callbacks. Surely those simple examples are easy-to-read because they are small, but as soon as we start adopting them in our application logic, we end-up having huge Activities which are highly coupled to the API’s which we are using.
That’s how swapping any of our app components, like UI, backend or business logic, become a big headache.
How to deal with it?
I have found quite some repositories on GitHub demonstrating how to build a MVC, MVP or a MVVM pattern in android, but all of them have the same problem: they add too much overhead to our code and instead of making the project more clear, they make it more complicated to understand. At the end of the day, the core of an android app is represented by its Activities. We just need to be careful in not making them too big when we add the domain-specific business logic to our app. So, for example, we can create modules and services which
- embed the business logic,
- act as intermediate with our data source,
- and are injectable in the Activities.
Here I am going to share my personal solution to this problem, which I have found to be quite convenient for many applications and in a lot of circumstances (like the one in which we need to replace Parse with a new BaaS).
First of all, I separate the data and business logic from the activities and from the model entities by creating the main packages: model, service and ui.
The model package contains only POJO’s classes for the entities used in the app.
The service package may contains entities validators, component injectors, actions to responds to user events, events to trigger UI changes on the activities, backend implementations and other utilities or helpers.
The ui package, instead, contains custom widgets, and all Activities and Fragments, but they are stripped out, as much as possible, from the code related to business or data logic, which is instead included in the service package described before.
How to make the Backend replaceable?
Having the above architectural design in mind, the specific BaaS will be transparent to our Activities and Fragments, because they will just use a BackendAdapter interface present in the package service->backend. The actual implementation of this interface will be injected by Dagger when the Activities are created.
In the service->injector->BackendModule.class, I define which concrete class will be provided when a BackendAdapter needs to be injected. For example we can decide to inject a FirebaseBackendAdapter, a ParseBackendAdapter or something else. All those specific implementations will be part of the package service->backend.
There is one last point to take into consideration while making the backend replaceable. Usually the BaaS’s API needs some callbacks or hooks to the UI in order to change the data on the screen. In a very high-coupled app, where those backend calls and callbacks are in the Activity class, it’s easy to get a reference to the elements on the screen. On the contrary, in a modular application, like the one that I am suggesting, we need to use a different strategy. I have been using three strategies until now, and I haven’t decided yet if one is better than the other, but it could be advantageous to use also more than one of them depending from the data flow of your app.
- It is possible for example to use a Publish/Subscribe pattern with an event bus, and fire events from the backend adapter to the activities when the UI need to be updated. This is the simplest approach to use, but it makes the activities bigger, because they need to hold the code which has to be executed when an event is received, so basically the onEvent methods.
- Another strategy consist in making the backend adapter to return Rx Observable objects when its methods are invoked by the Activities. In such a way the Activities can subscribe on those observable objects to start listening the data flow. This approach is similar to the one above because it is also based on the Observer Pattern, but the events instead of being fired to the whole app, they are received only by the specific component which is interested in it. Moreover, RxAndroid offer lots of operator to easily transform the received data, which help in reducing the code that need to be written, but it requires some start up time to get familiar with the possibility offered by Rx.
- Alternatively, the Activities can pass to the backend adapter’s methods some “functions” that need to be executed when data are ready. This “passing of functions” in android can be implemented with an Action interface and a set of concrete implementations like ShowLoginScreenAction, ShowItemListScreenAction etc.. which will perform UI changes. This approach, based on the Command Pattern, helps in extracting some code from the activities to some specific command class, but it requires to pass those command as input to the backend adapter methods. This aspect is actually not too bad, because it allows to customize more easily the behavior of the backend by injecting every time the commands that we want to execute.
Ok, this is a lot of theory, but I think that it was necessary to easily understand my design choices and the advantages in designing an application with replaceable components and low coupling.
Now it’s time to look at some code. In this working in progress GitHub repo I have implemented what I have described above.
What alternative BaaS can we use?
The answer is Firebase:
- It’s backed by Google;
- there is a big community behind it;
- it requires only a few lines of code to get started;
- and there are plenty of resources to learn about it.
If you have a little bit of time I recommend to watch this very well done Google course on Udacity, Firebase Essentials for Android, which I have personally found to be quite interesting.
Otherwise, if you don’t have much time and you just want to get started, look at my simple example on GitHub. In that example I show the whole authentication flow (account creation, login and logout) with Firebase, storing of POJO’s objects, and retrieval of data by a RecyclerView adapter which automatically sync with Firebase.
Feel free to fork it and use it as a starting point for your future cloud-based app. Also don’t hesitate to share feedback or PR’s to keep improving it.
As I said, it’s a work in progress, therefore I will keep adding features to it during the coming weeks. For now have fun and MOVE AWAY FROM PARSE!!! 🙂