MVI Predates Redux, and Most Android Implementations Get It Right
This article is translated from "Yes, That's MVI: The Pattern's Full History, Misconceptions, and Modern Android Form", original link https://proandroiddev.com/yes-that-is-mvi-674f810ca4fe, published by Eury Pérez Beltré on May 29, 2025.
Behind every tradition lies a reason—and behind the reason, a story. – Anonymous
Welcome to this article! My goal is to help you truly understand the essence of the MVI pattern, explain why I am confident in my views, and the origins of the pattern. But before you dive in, please note:
📝 This article contains extensive and in-depth research into the history and evolution of the MVI architectural pattern, as well as the author's opinions. Be prepared to distinguish facts from opinions 😉
Table of Contents
Introduction: Beyond the Buzzword
Architectural Precursors: The Embryonic Form of MVI
The Origin of MVI: How the Pattern Formed
The Problem: Misconceptions about MVI
MVI in Android: What It Really Looks Like
Conclusion: My Personal Insights
Introduction: Beyond the Buzzword
MVI has become a powerful architectural pattern in Android development, offering clarity and predictability in managing state and UI complexity. When applied correctly, it provides a robust mental model that helps developers build maintainable and scalable applications.
However, those I call the "MVI Police" insist that only implementations strictly following an MVU or Redux style count as true MVI, dismissing other valid approaches as mislabeled. This narrow view ignores MVI's true origins and how it can flexibly adapt to different application scenarios without losing its core principles.
In this article, we will explore the origins of MVI, the problems it aims to solve, and how embracing its spirit (rather than rigid rules) helps developers write clean and efficient architecture. After all, good architecture is about solving problems, not adhering to dogma.
- Architectural Precursors: Patterns That Led to MVI
MVI was not introduced in a paper or book we can reference like other design patterns. It was the result of deliberate thought by its creator, who took issue with the existing solutions of the time (1, 2, 3).
While many consider MVI a direct descendant of Redux (2015) or Elm's MVU (2011), its true conceptual roots lie in two earlier patterns: Model-View-Controller (MVC) and Flux, according to André Staltz (creator of cycle.js and the MVI pattern).
Model-View-Controller (MVC) — circa 1979
As one of the earliest and most influential architectural patterns, MVC divides an application into three main components:
- Model: Manages data and business logic
- View: Responsible for rendering UI elements
- Controller: Handles user input and updates the model accordingly
André Staltz revisited MVC through reactive programming. In his 2014 blog post "Reactive MVC and the Virtual DOM", he proposed a modern rethinking of MVC tailored for stream-based UIs. In this re-imagining:
- Intent replaced the Controller: it captures user interactions as event streams.
- Model reacts to these streams to update application state.
- View reactively renders the state.
He called this restructured architecture Model-View-Intent (MVI), making it the first formal mention of the pattern. This version of MVC respects unidirectional data flow while aligning naturally with observable-based systems like RxJS.
Flux (MVC) — May 2014
Shortly before Staltz's blog post, Facebook introduced Flux and React. React focused on UI rendering, while Flux introduced a new architecture for managing data flow:
- Actions describe what happened.
- Dispatcher routes actions to stores.
- Stores hold application state and update in response to actions.
- Views listen for store changes and re-render.
Flux's most important innovation was unidirectional and circular data flow, which brought order to the chaos of shared mutable state in UI applications. Staltz acknowledged this innovation but criticized Flux for often mixing imperative and reactive paradigms. Nonetheless, the principle of keeping state changes predictable and linear became a key part of MVI.
In MVI, Staltz adopted Flux's unidirectional data flow but simplified it. He removed the Dispatcher, avoided imperative stores, and instead used observables to model user intents, state, and UI rendering directly as pure reactive transformations. In his own words:
"The React/Flux combo is clearly inspired by reactive programming principles, but the API and architecture are an unreasonable mix of interactive and reactive patterns... We can do better."
- The Origin of MVI: How the Pattern Formed
The MVI pattern was introduced by its creator André Staltz around 2014 in the JavaScript framework Cycle.js, a year before Redux was born.
The MVI Pattern in Practice
Model-View-Intent (MVI) is a reactive UI architecture pattern that enforces a single, immutable state (Model), a unidirectional flow of Intents (user actions) into the system, and a View that renders each state.
- Model holds the application state.
- View renders the state.
- Intent replaces the controller as a stream of user interactions.
You can read the definition of MVI and its components directly from its creator's blog post about architectural patterns written in 2015.
Flux's focus on immutable state and unidirectional flow influenced MVI's design, which formalized the cycle: user intent → state change → UI rendering. This synthesis of ideas from classic MVC, Flux, and reactive programming principles gave birth to MVI—not as a derivative of Redux (which came later) or Elm, but as a distinct architecture tailored for reactive systems.
Code Example
In a blog post, Hannes Dorfmann explained André Staltz's earliest ideas about MVI and expressed the idea as a mathematical expression:
This expression is excellent for understanding how MVI components interweave and represent dependencies on each other: Intent is passed to the Model, which generates a new State, and provides it to the View to render it. This mathematical expression encapsulates the spirit of MVI.
A Cycle.js implementation looks similar to:
You can see this mathematical expression represented literally in lines 44-46.
This looks good, but how does this idea actually translate to Kotlin?
Well, not so fast, we'll get to that. For now, let's continue our unveiling journey...
- The Problem: MVI Misconceptions
As the MVI pattern grew in popularity within the Android ecosystem, especially with the rise of Jetpack Compose, coroutines, and unidirectional state management, a wave of skepticism emerged. Some developers (dubbed the "MVI Police") began questioning whether many Kotlin/Android implementations could truly be called "MVI."
The MVI and Redux Misconception
One of the most common misconceptions is that MVI is just Redux but with a different name, or that MVI was directly inspired by Redux. But as you saw in the previous sections, this is historically inaccurate: MVI was introduced by André Staltz in 2014 in his Reactive MVC blog post, months before Redux was released later in 2015 by Dan Abramov.
That said, it's easy to understand the source of the confusion. MVI and Redux do share several core ideas—but this is not because MVI derived from Redux. Rather, both were independently influenced by the same underlying architecture: Flux.
These similarities include:
- MVI treats user intents similarly to Redux actions.
- MVI model state is immutable and evolves over time, just like Redux's store.
- MVI uses reducers to derive new state from previous state and user input.
However, within these similarities lie key architectural differences:
By now you should agree that Kotlin/Android MVI implementations do not need to strictly follow the Redux design pattern.
The MVI and MVVM Misconception
Another common misconception is that many popular MVI implementations are just MVVM with a single state data class and a sealed interface for intents. To clarify this, let's first review the basics of what a design pattern truly is.
According to the seminal 1994 book Design Patterns: Elements of Reusable Object-Oriented Software by the Gang of Four:
"Design patterns systematically name, motivate, and explain general designs that address recurring design problems in object-oriented systems."
"A design pattern is a general repeatable solution to a commonly occurring problem in software design. It isn't a finished design that can be transformed directly into code; it is a description or template for how to solve a problem that can be used in many different situations."
Just like patterns found in nature—whether numerical, visual, or otherwise—mutating or modifying an existing pattern often produces a new, distinct pattern:
So, are developers following the MVI trend actually unconsciously doing MVVM? To address this, let's compare the similarities and differences between the two patterns, just as we did in the previous section.
To keep the perspective grounded, we will reference a highly respected Microsoft article — MVVM dates back to 2005.
Both patterns share several common characteristics:
- Explicit separation of concerns
- Use of observable streams to update the UI
- Use of ViewModels (especially on Android)
However, when we examine the details side-by-side, we find that despite these superficial similarities, MVI and MVVM are fundamentally different in key aspects—demonstrating how the evolution of patterns leads to entirely new architectural approaches.
As software development often teaches us, things that look similar at first glance reveal distinct differences upon closer inspection.
A clean and simple MVVM implementation in Android looks like this:
This idea is supported by a diagram the Android team created some time ago, intended to suggest an architectural pattern for applications:
Notice the multiple LiveData instances in the viewModel. Now to see an MVI example, let's jump to the next section.
- MVI in Android: What It Really Looks Like
After deep research and comparison with related patterns, we can confidently identify the core characteristics that define a true MVI implementation:
- Single immutable state object: This state is centralized, typically residing in the ViewModel.
- UI interactions as Intents: All user actions are represented as data streams, typically sealed classes or interfaces.
- Reducer function: A pure function that takes the previous state and an Intent, then returns a new State (
Old State + Intent → New State). - Unidirectional data flow: The architecture strictly follows the flow of "Intent → Model → State → View."
- Side effects handled separately: Effects such as navigation, network calls, or displaying messages are explicitly modeled and processed outside the reducer.
I can already hear some objections, but let's put theory aside and see what it looks like in code.
MVI Components as a Model
For our example, we will define three essential components:
**UserUiState**: An immutable state model exposed as a Kotlin Flow.**UserIntent**: Represents all possible user actions in the UI.**UserSideEffect**: Defines side effects that occur outside the pure state update logic.
The ViewModel Role in MVI
In an Android MVI function, the ViewModel is responsible for:
- Exposing state as an immutable, observable stream.
- Exposing side effects as a separate immutable stream.
- Providing a public function to accept and process Intents, applying reducer logic to update the state accordingly.
As you may have realized by now, this is very similar to the implementation some have criticized as "not real MVI." But the historical context and evidence compiled in this article show otherwise: the community has been doing the right thing. Perhaps it's time for the MVI Police to revisit their own definition of the pattern.
📝 Note: I do not recommend this side effect implementation, but for educational purposes, I'll keep it simple. Read my article on best practices for one-off events for an in-depth explanation on this topic.
- Final Thoughts: My Personal Take
Although I never identified with the "MVI Police" mindset, I realized I lacked solid information to back up my views. This extensive investigation taught me some valuable lessons—some not even about design patterns or software engineering. I'd like to share a few:
- In software development and in life, what sounds logical to you is not always correct.
- Concepts have history and evolve over time; recognizing this process is crucial.
- Before telling someone they are wrong simply because you believe your way is the only right way, think twice—sometimes even three times. Multiple valid approaches can coexist.
On the technical side, the biggest takeaway is actually about soft skills:
- Design patterns are repeatable solutions to common problems. If the solution changes significantly, it becomes a new pattern.
- Yes, changes like using a single immutable data class for state, a sealed interface for intents, and a reducer function inside a ViewModel can justify defining a different pattern.
- Yes, you can adapt/personalize a pattern and still refrain from telling others your implementation is the "real" or "best" one.
- History matters. Many who claim MVI was inspired by Redux may not even realize that MVI existed first.
- Finally, an MVI implementation must not include a global store like Redux. Doing so means it is not MVI. (Yes, I'm looking at you, MVI Police—come on! 🚓)
- The creator of MVI didn't even like Redux from the start.
- MVI is simple and clear. Redux, not so much...
Ultimately, what truly matters is understanding the reasons behind these patterns, respecting their evolution, and thoughtfully applying them to solve real problems. Let's embrace diversity of ideas rather than following a "correct way." This approach will foster healthier discussions and ultimately lead to better software.
Follow me on Medium and LinkedIn to know when I write the article showing you how I personally leverage MVI in projects.
If this article helped you, please like, comment, and share 😉
Sources:
- https://staltz.com/cycleconf17/#/step-1
- https://www.futurice.com/blog/reactive-mvc-and-the-virtual-dom
- https://staltz.com/blog
- https://staltz.com/unidirection-user-interface-architectures
- <https://staltz.com/nothing-new-in-react-and-flux-except-one-thing
- https://staltz.com/some-problems-with-react-redux
- https://hannesdorfmann.com/android/model-view-intent/
- https://redux.js.org/understanding/history-and-design/history-of-redux
- https://legacy.reactjs.org/blog/2014/07/30/flux-actions-and-the-dispatcher.html
- https://learn.microsoft.com/en-us/archive/blogs/johngossman/introduction-to-modelviewviewmodel-pattern-for-building-wpf-apps
- https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/february/patterns-wpf-apps-with-the-model-view-viewmodel-design-pattern
- https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel
Welcome to search and follow the public account 「稀有猿诉」 for more high-quality articles!
Protect originality, please do not reprint!