La ontología de MVI
El mapa mental de Model-View-Intent en Kotlin y Android: Orbit, coroutines y Flow, estado inmutable en Compose, side effects de una sola vez y Kotlin Multiplatform.
Android llegó al flujo unidireccional por el camino largo. Primero fue MVC, luego MVP con sus interfaces infinitas, luego MVVM con observables por todas partes, y en cada salto quedaba el mismo poso de bugs: dos fuentes de verdad que discrepan, un evento que se repite al rotar la pantalla, un estado que nadie sabe quién cambió. MVI cierra ese ciclo tomando prestada la lección de Elm: una sola entrada (la intención del usuario), un solo estado inmutable, y una función pura entre medias. Este mapa te da la visión aérea antes de recorrerlo.
- Ver el mapa mental completo de MVI en el ecosistema Kotlin.
- Entender por qué el estado inmutable encaja tan bien con Compose.
- Distinguir estado de side effects de una sola vez.
- Situar Orbit, las coroutines, Flow y Kotlin Multiplatform.
El territorio, de un vistazo
mindmap
root((MVI))
El ciclo
Intent
Reduce
Estado inmutable
Side effects
Kotlin
data class
sealed interface
Coroutines
Flow y StateFlow
Android
ViewModel
Compose
Estabilidad
Navegacion
Orbit
Container
Testing
Sin libreria
Escala
Capa de datos
Inyeccion
MultiplatformLas ideas clave
Una sola entrada
Todo lo que puede cambiar la pantalla entra por el mismo sitio: un intent. No hay setters dispersos ni métodos públicos que muten el estado por la puerta de atrás.
Un estado inmutable
El Model es un data class que se reemplaza entero con copy. La UI recibe una foto completa y coherente, nunca un puñado de observables que pueden estar desincronizados.
Los eventos no son estado
Navegar o mostrar un aviso ocurre una vez y se acabó. Guardarlo en el estado es el bug clásico que reaparece al rotar. MVI los separa en un canal aparte.
Compose lo pide a gritos
Una UI declarativa que es función del estado encaja de forma natural con un estado inmutable único. El skipping de recomposición casi funciona solo si el modelo está bien hecho.
Por qué importa
Hay una prueba brutalmente simple que revela si una arquitectura Android está bien pensada: gira el dispositivo en cada pantalla de tu app. Si algo parpadea, se pierde, se duplica o vuelve a mostrarse, has encontrado un defecto de diseño de estado, no un capricho del sistema. Android destruye y recrea la Activity, y con ella todo lo que no hayas modelado explícitamente como estado sobreviviente. Esa crueldad, que durante años se vivió como una tortura de la plataforma, es en realidad un regalo: te obliga desde el primer día a responder preguntas que en otras plataformas puedes posponer años. ¿Qué parte de lo que ves es estado y qué parte es un evento que ya ocurrió? ¿De quién es la fuente de verdad? ¿Qué debe sobrevivir a la muerte del proceso y qué no? MVI se lleva bien con Android precisamente porque esas preguntas son su punto de partida, no una corrección posterior. El estado es un valor único que puedes serializar y restaurar; los eventos de una sola vez viven en un canal que se consume y se olvida; y el reducer es una función pura que puedes ejecutar mil veces en un test sin abrir el emulador. La ceremonia existe y es real —hay que nombrar cada intent, declarar cada efecto—, pero a cambio la pregunta “por qué se ve esto así” siempre tiene una respuesta que cabe en una función.
El camino
- Niveles 1–2 · Fundamentos — el patrón MVI y Orbit como implementación idiomática en Kotlin.
- Niveles 3–5 · Kotlin — data classes y sealed, coroutines y concurrencia estructurada, Flow y StateFlow.
- Niveles 6–8 · Android — el ViewModel, el estado en Compose, y la estabilidad que gobierna la recomposición.
- Niveles 9–13 · El ciclo — intents y reducción, side effects de una sola vez, Orbit a fondo, su testing, y MVI a mano.
- Niveles 14–18 · A escala — navegación, capa de datos, inyección de dependencias, Multiplatform y testing.
- Niveles 19–21 · Perspectiva — la comparación con MVVM y MVP, los errores comunes, y la síntesis final.
- Abre una app tuya y gírala en cada pantalla. Anota todo lo que se rompe, parpadea o se repite.
- Para cada fallo, decide: ¿esto era estado que debí conservar, o un evento que no debí guardar?
- Coge una pantalla y escribe su estado como un solo
data class; comprueba si alguna combinación de campos es imposible. - Comprométete con la separación: el estado es una foto, los efectos son un disparo.