MVI Architecture with Jetpack Compose

Sinan Yılmaz
Huawei Developers
Published in
6 min readDec 4, 2023
Photo Anders Jildén by on Unsplash

Introduction

When we are new to software, we often struggle with questions such as which language should learn, and which language is used more. This question then continues as to which architecture should I learn.

There is no exact right answer to these questions. It would not be wrong to say that the important thing is the need. Just like programming languages are tools, I think the same is true for architectures. Each architecture has a problem solution that it focuses on, a shining aspect. In this direction, I would like to talk about MVI in this blog with the motto “We have such an option, it might work”.

MVI (Model-View-Intent)

MVI is a software architecture that emerged in the 1990s and aims to develop applications with a reactive approach. As with any architecture, MVI focuses on certain ideas as they emerge. Let’s take a look at these first.
The structure of MVI is similar to MVC at some points. One of the reasons for this is of course that it is inspired by MVC.

from cycle.js.org

In the diagram above, we are shown what the MVC layers do, including the user. Here the Model and View have one task, while the Controller has 2 different tasks: manipulating the Model and View. This is exactly one of the points MVI focuses on. The goal of MVI is that each layer should not take on a task other than sending/receiving data, they should be independent of each other.

from cycle.js.org

Considering the years and needs of MVC, its structure may be sufficient. However, this is not possible and logical in today’s ecosystem. In MVI, the need to achieve this solution is simply shown in the diagram above. An input operation and then an output operation. From this perspective, what we need is functionality.

Now let’s examine the layers of MVI one by one.

Model

In MVI, models are positioned differently than in other architectures. For example, in MVVM we use the model layer to hold data. In MVI, models also represent the state of the application. So, it both holds the data and represents the app state. Therefore, they should be immutable to ensure data flow between them and between other layers.

There can be some problems in scenarios where models only hold data. Especially in large applications, problems can be encountered in the case of multiple inputs/outputs.

This is another issue that MVI is focused on solving. It aims to prevent such situations by including models that not only hold data but also state in the models.

HomeState.kt sealed class

As we can see in the example above, we can create our Loading, Success, and Error states in a sealed class. This is our state.

View

As in any architecture, the View layer is an important part of the user interface in MVI. It separates Model and Intent and plays an important role in the separation of concerns.

  • It brings the information it receives from the model together with the user in the user interface.
  • It captures the user’s interactions and transmits them to the intent layer.

Intent

First of all, intents are not Android’s android.content.Intent class :)

Intents in MVI represent a future action that changes the state of the application. It takes the user interactions from the view, converts them into a format that the model can understand, and transmits them to the model layer. In other words, it would not be wrong to say that it is a structure that encapsulates the user’s interaction.

Now, in light of this information, let’s see how we can update the diagram we showed above.

The user engages in an interaction. This interaction reaches intents. Intents transmit these changes to the state/model. This then presents the updates to the user via the view.

sealed class NoteIntent {
data class AddNote(val note: String) : NoteIntent()
data class DeleteNote(val note: String) : NoteIntent()
}

MVI Core Principles

We can define the basis of MVI with 3 principles. You can see them between the lines above.

  • Functionality
  • Immutable
  • Reactive

These 3 features are important points for the formation of MVI. We have seen above that we need functionality to provide a direct output based on user interaction. Functional programming returns us a direct value and this helps us for a fast process.

Immutable objects make thread management easier. Reactive programming also safely parallelizes work. We can reach everywhere safely. Those who have used RXJava, a product of reactive programming, are already familiar with this.

Advantages of MVI

  • Since all layers have a separate function in a single direction, separation between layers is better. In this way, the separation of concerns is implemented well.
  • With the effect of the above distinction, testability becomes easier.
  • Thanks to one-way data flow, predictability increases, and you can predict what you will encounter next.
  • It enables a consistent user interface thanks to one-way data flow.
  • After all the layers are separated, the project becomes easier to control in a more modular structure and becomes scalable.

Disadvantages of MVI

  • Due to the difference in its structure, the learning curve may be difficult at first compared to other architectures.
  • Separately created intents and states can increase boilerplate code at a certain point.
  • Complexity may increase due to boilerplate codes, especially as the application grows.

A Declarative UI: Jetpack Compose

The development of Declarative UIs has increased recently not only on Android but also on iOS. Jetpack Compose is one of them. The concept of state is very important in Compose. It is inevitable to use it especially during the recompose process of composables.

In this respect, their philosophies overlap with MVI, and strong structures can be established with MVI, as in MVVM.

MVI with Jetpack Compose

  • As we know, Compose is a Declarative UI product. MVI works compatible with Declarative structures.
  • We mentioned the predictable structure above. With MVI and Compose, we have predictable state management.
  • Thanks to the influence of MVI and the state structure, we do not have much difficulty in state management.

MVI vs MVVM

They both have different superpowers🦸🏻‍♀️🦸🏻

MVVM -> data binding and view state management
MVI -> one-way data flow and powerful state management

But of course, it has different aspects. For example, while user actions are managed with intents in MVI, operations are performed through the ViewModel in MVVM.

If we look at state management, there is a single and central state management object in MVI. MVVM also has a central state object, but state changes are made directly from the ViewModel.

Conclusion

As we see, the approach of MVI architecture is slightly different from others. This difference can provide some advantages, especially when using Compose. What matters is what you and your project need.

Thank you for reading this far, don’t forget to clap if you liked it🙌

--

--