Best Architecture For Android : MVI + LiveData + ViewModel = ❤️

Best of MVVM and MVI architecture patterns merged into one, for a perfect architecture for any Android project.

Rohit Surwase
ProAndroidDev

--

Photo by Tim Mossholder on Unsplash

TL;DR
If you are already aware of basic principles of architecture patterns and MVVM and MVI patterns in detail then skip the basics and jump to MVI + LiveData + ViewModel (or second) section of the article.

Preface

There are so many architecture patterns available, each has some pros and cons. All those patterns try to achieve the same basic principles of architecture:

1. Separation of concerns (SoC): It is a design principle for separating a computer program into distinct sections such that each section addresses a separate concern. A concern is anything that matters in providing a solution to a problem.

This principle is closely related to the Single Responsibility Principle of object-oriented programming which states that “every module, class, or function should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class, module or function.” -Wikipedia

2. Drive UI from a model: App should drive UI from a model, preferably a persistent model. Models are independent from the View objects and app components, so they're unaffected by the app's life-cycle and the associated concerns.

Let’s also look at the summary of some popular architecture patterns-

⭐ MVC Architecture:

Model-View-Controller architecture by Trygve Reenskaug is the base of all modern architecture patterns. Let’s see the responsibility of each component defined on it’s Wikipedia page-

* The model is responsible for managing the data of the application. It receives user input from the controller.
* The view means presentation of the model in a particular format.
* The controller responds to the user input and performs interactions on the data model objects. The controller receives the input, optionally validates it and then passes the input to the model.

So, the model is responsible for representing state, structure, and behavior of the view and view is nothing but the representation of that model.

⭐ MVVM Architecture:

In Model-View-ViewModel architecture, view has the instance of ViewModel and it calls respective function based on the user input/action. Also, view observes different observable properties of ViewModel for changes. ViewModel processes user input based on the business logic and modifies respective observable property.

⭐ MVI Architecture:

In Model-View-Intent architecture, view exposes view-events (user input/action) and observes model for view-state changes. We process view-events and convert them into respective intent and pass it to the model. The model layer creates a new immutable view-state using intent and previous view-state. So, this way it follows the Unidirectional Data Flow principle i.e. data flows only in one direction: View > Intent > Model > View.

In summary, the best part of MVVM architecture is ViewModel but I think, it does not follow the concept of model defined in MVC pattern, as, in MVVM DAO (Data Access Object) abstraction is considered as model and view observes multiple observable properties from ViewModel for state changes and view is not driven by model, directly. Also, these multiple observable properties from ViewModel can cause state overlapping issue (two different states being shown unexpectedly).

MVI pattern solves this problem by adding an actual ‘model’ layer which is observed by view for state changes. As this model is an immutable and single source of truth for the current view state, state overlapping will not happen.

In the following architecture, I tried to combine the best of MVVM and MVI patterns for a better architecture of any Android project, on top of that I abstracted as many things as possible by creating base classes for View and ViewModel.

🌟 MVI + LiveData + ViewModel = ❤️ Architecture:
Before proceeding, let’s reemphasize some basic terms from MVI architecture:
ViewState: As the name suggests this is part of the model layer, and our view observes this model for state changes. ViewState should represent the current state of the view at any given time. So this class should have all the variable content on which our view is dependent. Every time there is any user input/action we will expose modified copy (to maintain the previous state which is not being modified) of this class. We can create this model using Kotlin’s data class.

ViewEffect: In Android, we have certain actions that are more like fire-and-forget, for example- Toast, in those cases, we can not use ViewState as it maintains state. It means, if we use ViewState to show a Toast, it will be shown again on configuration change or every time there is a new state unless and until we reset its state by passing ‘toast is shown’ event. And if you do not wish to do that, you can use ViewEffect as it is based on SingleLiveEvent and does not maintain state. ViewEffect is also part of our model, and we can create it using Kotlin’s sealed class.

ViewEvent: It represents all actions/events a user can perform on the view. This is used to pass user input/action to the ViewModel. We can create this event set using Kotlin’s sealed class.

I suggest you, keep these three classes in a single file as it will give you an overall idea of all doable actions and variable content being handled by the target view.

Now, let’s dive deeper into the architecture:

The above diagram may have given you the core idea of this architecture. If not, the core idea of this architecture is, we are including an actual immutable model layer in the MVVM architecture and our view is dependent this model for state changes. This way ViewModel will have to modify and expose this single model.

To avoid redundancy and simplify the use of this architecture in multiple places, I have created two abstract classes one for our view (separate for activity, fragment, custom-view) and one for ViewModel.

AacMviViewModel: A generic base class to create ViewModel. It needs three classes STATE, EFFECT, and EVENT. We have already seen an example of these classes above.

As you can see we have viewState: STATE and viewEffect: EFFECT and two private LiveData containers _viewStates: MutableLiveData<STATE> and _viewEffect: SingleLiveEvent<EFFECT> which are being exposed using public functions viewStates() and viewEffects(). Please note we are extending AndroidViewModel as it will allow us to use the Application context (only) if required. Moreover, we are logging each viewEvent, that we will be processing.

This is how we create a ViewModel for any of our activity/fragment/view:

Implementation of AacMviViewModel class

All we have to do is initialize viewState inside init{} block and modify the viewState further using copy() function of the data class whenever required. Please do not modify the same instance of viewState instead modify its copy, this way we are keeping ViewState immutable. If you modify the same instance of viewState, you may encounter an unexpected behavior as you may change some properties which are being processed by the view.

viewState = viewState.copy(fetchStatus = FetchStatus.Fetched, newsList = result.data)

That’s it, the rest of the part (i.e. _viewStates.setValue() ) is being handled by the AacMviViewModel class.

AacMviActivity: A generic abstract class to create compatible Activities for this architecture.
(Please refer this repository for generic classes required for fragment and custom-views.)

It has viewModel , renderViewState() and renderViewEffect() abstract properties/functions which we need to implement. Also, it creates viewStateObserver, viewEffectObserver LiveData-Observers internally and starts observing viewStates() and viewEffects() exposed by the ViewModel in onCreate() of the Activity. So, this abstract activity does everything that we would have to do anyway inside each activity. Moreover, it logs each observed viewState and viewEffect.

Now, creating a new activity for this architecture is very easy:

Implementation of AacMviActivity class

That’s all, with this we have everything in place, working seamlessly, logging each action and content we are processing. No chance of state overlapping as the model is a single source of truth for the view’s state change.

Note: If you are new to this ‘model-driven UI’ you may think we are adding more complexity than straight forward processing as for some complex views ViesState data class will have so many properties as it has to have content for each widget and its visibility and so on. But believe me, it will pay off as tracking down the cause of any issue/crash will be super easy.

Further Improvements:
1. Many of you would suggest using ConflatedBroadcastChannel as it posts the latest values only but it is still experimental so I preferred to use LiveData. And anyway even if we decide to use ConflatedBroadcastChannel/Flow in the future, we will have to modify the abstract classes only.

2. What if we have single activity and multiple fragments or custom-views? How can we communicate between activity-to-fragment, fragment-to-fragment? F̵o̵r̵ ̵t̵h̵i̵s̵ ̵u̵s̵e̵ ̵c̵a̵s̵e̵,̵ ̵w̵e̵ ̵h̵a̵v̵e̵ ̵i̵n̵t̵r̵o̵d̵u̵c̵e̵d̵ ̵D̵a̵t̵a̵S̵t̵o̵r̵e̵,̵ ̵m̵o̵r̵e̵ ̵o̵n̵ ̵t̵h̵i̵s̵ ̵i̵n̵ ̵t̵h̵e̵ ̵n̵e̵x̵t̵ ̵a̵r̵t̵i̵c̵l̵e̵.̵
Update: The moment I started to write about our DataStore implementation, I realized it was breaking MVI’s core concept, so dropped that idea.

For a detailed version (with some improvements) of this architecture that handles Activities, Fragments, and Custom Views with an example, please check out this GitHub repository.

Credits & References:
⚈ Special thanks to my colleagues Aalap Shah and Akhilesh Swarnkar for questioning each component/concept that I have used and helping me evolve this architecture. We had so many discussions over the incremental iteration of this architecture. Thanks to both of you for asking me to minimize and simplify as many things as possible so further implementation becomes easy.
Official guide on MVVM Architecture
MVI Architecture by Hannes Dorfmann
MVI Architecture by Etienne Caron & this example
MVC Architecture - Wikipedia
Concept of ViewEffect by Kaushik Gopal
Kotlin Backing Properties
⚈ Wikipedia- Separation of concerns & Single responsibility principle
GitHub: source code and example

Thanks for reading!
Rohit Surwase

If you liked this article, clap clap clap 👏👏👏 as many times as you can.

--

--

Techie by Birth. Writer by Hobby. Observer by Nature. Unpredictable by Character. Android Developer (Google Certified) by Profession.