MVI — Model-View-Intent patternining mobil ilovalardagi mohiyati

Muallif: IT Sectr Nashr etilgan: 2026-02-16 O'qish vaqti: 10 daq

MVI (Model-View-Intent) — bir tomonlama ma'lumot oqimi va o'zgarmas holatga asoslangan reaktiv arxitektura patterni. MVVM dan farqli o'laroq, ViewModel bir nechta StateFlow ga ega bo'lishi mumkin, MVI yagona holat (State), o'zgarmas niyatlarni (Intent) va sof reduksiya funksiyasini (Reducer) belgilaydi. MVI istalgan vaqtda ekran holatining oldindan bashorat qilinishini kafolatlaydi. Pattern Mosby va Orbit kutubxonalari tomonidan Android hamjamiyatida ommalashtirilgan. Batafsil — Arkadii Ivanov dan MVIKotlin.

Asosiy

  • MVI — Model (holat), View (ko'rsatish), Intent (foydalanuvchi niyati) — reaktiv sikl
  • Unidirectional data flow — ma'lumotlar bir yo'nalishda harakatlanadi: Intent → Reducer → State → View
  • Immutable State — ekran holati — har bir o'zgarishda qayta yaratiladigan o'zgarmas obyekt
  • Reducer — joriy holat va Intent ni qabul qiluvchi, yangi holat qaytaruvchi sof funksiya
  • Side effects — yon ta'sirlar (tarmoq, MB) Reducer dan alohida, Middleware orqali qayta ishlanadi

MVI nima: Model-View-Intent patternining mohiyati

MVI (Model-View-Intent) — Redux va Cycle.js tamoyillari asosida qurilgan reaktiv arxitektura patterni. Model — ekranning o'zgarmas holati, Intent — foydalanuvchi yoki tizim niyati, View — holatga obuna bo'lish va Intent jo'natish. Ma'lumotlar sikl bo'ylab harakatlanadi: foydalanuvchi View bilan aloqa qiladi → View Intent yaratadi → Intent Reducer tomonidan qayta ishlanadi → Reducer yangi holat yaratadi → View yangi holatni oladi va qayta chizadi.

MVI ning MVVM dan asosiy farqi — yagona haqiqat manbai (Single Source of Truth). MVVM da ViewModel bir nechta LiveData/StateFlow (userState, loadingState, errorState) ga ega bo'lishi mumkin, bu nomuvofiqlikka olib keladi: loading=true va user=null bir vaqtda. MVI da ekranning butun holatini tavsiflovchi aynan bitta sealed class/interface State mavjud. Istalgan vaqtda ekran holati bir ma'noda aniqlangan — yuklangan ma'lumotlar mavjud bo'lganda loading=true olish mumkin emas. IT Sectr da biz MVI ni murakkab mantiqli ekranlar uchun qo'llaymiz — buyurtma blankalari, ko'p bosqichli ro'yxatga olishlar, moliyaviy ekranlar — holatning oldindan bashorat qilinishi muhim bo'lgan joylarda.

KomponentMVI dagi rolMisol
IntentFoydalanuvchi yoki tizim niyatiLoadUser, Refresh, SubmitForm
StateEkranning o'zgarmas holatisealed class UserState
ReducerSof funksiya: State + Intent → Statefun reduce(state, intent) -> state
MiddlewareYon ta'sirlarni qayta ishlashTarmoq so'rovi, MB ga yozish

MVI sikli besh bosqichdan iborat: 1) View Intent jo'natadi (masalan, LoadUser(42)); 2) Middleware (EffectHandler) yon ta'sirni bajaradi — tarmoq so'rovi; 3) Natija tizimga yangi Intent sifatida qaytadi; 4) Reducer joriy holat va Intent ni qabul qilib, yangi holat yaratadi; 5) View yangi holatni oladi va qayta chizadi. Har bir bosqich oldindan bashorat qilinadi va alohida sinovdan o'tkaziladi.

Android da MVI: Kotlin da Intent, Reducer, State

Android da MVI Intent va State uchun sealed-sinflar, MVI mantiqli ViewModel va reaktiv ko'rsatish uchun Jetpack Compose orqali amalga oshiriladi. ViewModel View dan Intent qabul qiladi, yon ta'sirlarni Middleware ga topshiradi, Reducer ni ishga tushiradi va StateFlow orqali yangi holatni e'lon qiladi. Jetpack Compose state o'zgarganda UI ni qayta chizadi — MVI sikli uchun ideal.

kotlin
// Intent — foydalanuvchi niyatlari
sealed interface UserIntent {
    data class LoadUser(val userId: Int) : UserIntent
    data object Refresh : UserIntent
}

// State — ekranning yagona holati
sealed interface UserState {
    data object Idle : UserState
    data object Loading : UserState
    data class Success(val user: User) : UserState
    data class Error(val message: String) : UserState
}

// Reducer — sof funksiya
object UserReducer {
    fun reduce(state: UserState, intent: UserIntent): UserState = when (intent) {
        is UserIntent.LoadUser -> UserState.Loading
        is UserIntent.Refresh -> UserState.Loading
    }
}

// MVI bilan ViewModel
class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    private val _state = MutableStateFlow<UserState>(UserState.Idle)
    val state: StateFlow<UserState> = _state.asStateFlow()

    fun process(intent: UserIntent) {
        val newState = UserReducer.reduce(_state.value, intent)
        _state.value = newState
        when (intent) {
            is UserIntent.LoadUser -> loadUser(intent.userId)
            is UserIntent.Refresh -> loadUser(/* oldingi ID */)
        }
    }

    private fun loadUser(userId: Int) {
        viewModelScope.launch {
            repository.getUser(userId)
                .onSuccess { user ->
                    _state.value = UserState.Success(user)
                }
                .onFailure { e ->
                    _state.value = UserState.Error(e.message ?: "Error")
                }
        }
    }
}

// View Intent jo'natadi
fun UserScreen(viewModel: UserViewModel) {
    val state by viewModel.state.collectAsState()
    LaunchedEffect(Unit) { viewModel.process(UserIntent.LoadUser(42)) }
    when (state) {
        is UserState.Loading -> CircularProgressIndicator()
        is UserState.Success -> UserCard((state as UserState.Success).user)
        is UserState.Error -> ErrorView((state as UserState.Error).message)
        is UserState.Idle -> Text("Press to load")
    }
}

Middleware va yon ta'sirlar — MVI da sof Reducer tarmoq so'rovlarini bajara olmaydi. Middleware (shuningdek EffectHandler yoki Bootstrapper) Intent ni qayta ishlaydi, yon ta'sirni bajaradi va siklga yangi Intent ni qaytaradi. Orbit MVI va MVIKotlin kutubxonalari sinovdan o'tkaziladigan effektlar bilan o'rnatilgan Middleware qo'llab-quvvatlashni ta'minlaydi. Middleware holda MVI qo'shimcha Intent va State strukturasi bilan MVVM ga aylanadi.

Arkadii Ivanov dan MVIKotlin — Kotlin Multiplatform uchun eng mashhur MVI kutubxonasi. Android, iOS, web va JVM ni qo'llab-quvvatlaydi. Komponentlarni taqdim etadi: Store (ViewModel), Bootstrapper (boshlang'ich effektlar), Reducer, Middleware. 2025 yil oktyabrigacha kutubxona GitHub da 2,5K yulduz to'plagan va tijorat loyihalarida, jumladan yirik Rossiya banklari ilovalarida qo'llaniladi. IT Sectr da biz umumiy biznes mantig'i bilan KMP cross-platform loyihalari uchun MVIKotlin dan foydalanamiz.

iOS da MVI: Swift da bir tomonlama oqim

iOS da MVI Combine-ViewModel holda, Intent → State sikli orqali amalga oshiriladi. View yopish (closure) orqali Intent jo'natadi, Reducer — sof funksiya, State — o'zgarmas maydonli struct. SwiftUI State o'zgarganda View ni qayta chizadi, bu qo'shimcha @Published xususiyatlarisiz MVI siklga mukammal mos keladi. iOS da MVI ayniqsa Redux (JavaScript) dan o'tgan SwiftUI dasturchilari hamjamiyatida mashhur.

swift
// State — o'zgarmas struktura
struct UserState: Equatable {
    var user: User?
    var isLoading = false
    var errorMessage: String?
}

// Intent — niyatlar bilan enum
enum UserIntent {
    case loadUser(id: Int)
    case refresh
    case userLoaded(User)
    case loadFailed(Error)
}

// Reducer — sof funksiya
func userReducer(state: UserState, intent: UserIntent) -> UserState {
    var newState = state
    switch intent {
    case .loadUser, .refresh:
        newState.isLoading = true
        newState.errorMessage = nil
    case .userLoaded(let user):
        newState.isLoading = false
        newState.user = user
    case .loadFailed(let error):
        newState.isLoading = false
        newState.errorMessage = error.localizedDescription
    }
    return newState
}

// Store — holatga egalik qiladi va effektlarni boshqaradi
final class UserStore: ObservableObject {
    @Published private(set) var state = UserState()
    private let service: UserService

    init(service: UserService) {
        self.service = service
    }

    func dispatch(_ intent: UserIntent) {
        // 1. Reducer holatni yangilaydi
        state = userReducer(state: state, intent: intent)
        // 2. Side effects (agar kerak bo'lsa)
        switch intent {
        case .loadUser(let id), .refresh:
            service.fetchUser(id: id) { [weak self] result in
                switch result {
                case .success(let user):
                    self?.dispatch(.userLoaded(user))
                case .failure(let error):
                    self?.dispatch(.loadFailed(error))
                }
            }
        default: break
        }
    }
}

TCA (The Composable Architecture) — Point-Free dan iOS uchun eng mashhur MVI amalga oshirilishi, SwiftUI va Combine asosida qurilgan. TCA Store, Reducer, Effect va Environment ni taqdim etadi. 2025 yil oktyabrigacha GitHub yulduzlari soni 13K dan oshadi — bu iOS da MVI uchun de facto standartdir. TCA Starbucks, Airbnb (qisman) va ko'plab indie loyihalarida qo'llaniladi. Qo'lda yozilgan MVI dan farqli o'laroq, TCA sinov, navigatsiya va yon ta'sir masalalarini tayyor hal qiladi.

iOS da MVI vs MVVM — TCA/MVI holatning oldindan bashorat qilinishini ta'minlaydi, lekin ko'proq shablon kod talab qiladi (Reducer, State, Action). @Published bilan MVVM oddiy ekranlar uchun soddaroq. IT Sectr da biz 80% ekranlar uchun MVVM va 20% murakkab ekranlar uchun MVI (TCA) ishlatamiz — moliyaviy tranzaksiyalar, ko'p bosqichli blankalar, drag-and-drop interfeyslari, holat xatosi foydalanuvchiga pulga tushishi mumkin bo'lgan joylar.

MVI ni MVVM bilan solishtirish: qachon MVI ni tanlash

MVI va MVVM bir vazifani hal qiladi — Presentation qatlamini tashkil qilish — lekin holatni boshqarishga turlicha yondashuvlar bilan. MVVM bir nechta reaktiv manbalarga (LiveData, @Published) ruxsat beradi, bu nomuvofiqlikka olib kelishi mumkin. MVI har bir daqiqada aynan bitta holatni kafolatlaydi, bu uni qat'iyroq va oldindan bashorat qiladigan qiladi, lekin kod hajmini oshiradi.

MezonMVVMMVI
HolatBir nechta LiveData/StateFlowYagona sealed class State
Ma'lumot oqimiIkki tomonlama (View → ViewModel, LiveData → View)Bir tomonlama (Intent → Reducer → State → View)
Yon ta'sirlarTo'g'ridan-to'g'ri ViewModel daMiddleware/EffectHandler orqali
SinovViewModel ning unit testlariReducer + Middleware ning unit testlari
Shablon kodMinimalReducer + State + Intent + Middleware

Qachon MVI ni tanlash — holat qat'iy deterministik bo'lishi kerak bo'lgan ekranlar: moliyaviy operatsiyalar, internet do'kon savati, har qadamda validatsiya bilan ko'p bosqichli blankalar. Ushbu stsenariylarda holat xatosining narxi (masalan, ikkita LiveData poygasi tufayli savat summasini bitta mahsulotsiz ko'rsatish) qo'shimcha kod narxidan yuqori. MVVM da siz jamoaning intizomiga, MVI da esa arxitekturaga ishonasiz.

Qachon MVVM yetarli — 80% standart ekranlar: foydalanuvchi ro'yxati, profil, sozlamalar, yangiliklar tasmasi. Bu yerda yagona holat ortiqcha, MVI ning qo'shimcha strukturasi esa rivojlanishni sekinlashtiradi. IT Sectr da qoida shunday: ekranda o'tishlar bilan 3+ mumkin bo'lgan holat bo'lsa (yuklash → ma'lumot → xato → takrorlash → yuklash → ma'lumot) — MVI. Ekranda 1-2 asinxron operatsiya bo'lsa — MVVM.

MVI ning eng yaxshi amaliyotlari va tipik xatolar

Sealed State — MVI ning eng yaxshi amaliyoti. Holat Loading, Success(data), Error(message) variantlari bilan sealed class/interface sifatida belgilanadi. Bu View ning nomuvofiq holatga tushmasligini kafolatlaydi — loading=true bo'lganda ma'lumotni ko'rsatib bo'lmaydi, chunki Loading va Success turli sinflar. Holatga tegishli barcha ma'lumotlar sealed variant ichida: Success foydalanuvchini, Error esa xato xabarini o'z ichiga oladi.

Reducer sof funksiya bo'lib qolishi kerak — API, MB, SharedPreferences chaqiruvlarisiz. Sof funksiya State va Intent ni qabul qiladi, State qaytaradi. Yon ta'sirlar (tarmoq, MB, navigatsiya, tostlar) Middleware da yoki Reducer chaqirilganidan keyin Store.dispatch da qayta ishlanadi. Agar Reducer yon ta'sirlar bilan ifloslangan bo'lsa, MVI sinovdan o'tkazilish va oldindan bashorat qilinish qobiliyatini yo'qotadi — afzalliklarsiz qo'shimcha strukturaga ega MVVM olasiz.

Tipik xatolar — State ni sealed class o'rniga nullable maydonli data class sifatida e'lon qilish: data class UserState(val user: User?, val isLoading: Boolean, val error: String?). Bu MVVM ekvivalenti, MVI emas — View maydon kombinatsiyalarini haqiqiylik uchun tekshirishi kerak. Sealed yondashuvida haqiqiy bo'lmagan kombinatsiyalar (isLoading=true va user!=null) tip darajasida mumkin emas. Ikkinchi xato — biznes mantig'ini Intent ga joylashtirish (Intent.LoadUserBeforeXHours) o'rniga oddiy buyruq Intent larini (Intent.LoadUser) yaratish, biznes mantig'ini esa Middleware ga joylashtirish.

Tez-tez so'raladigan savollar

MVI ning MVVM dan asosiy farqi nima?

MVI yagona o'zgarmas sealed State sinfi va Reducer orqali bir tomonlama ma'lumot oqimidan foydalanadi. MVVM ikki tomonlama aloqa bilan bir nechta LiveData/StateFlow ga ruxsat beradi. MVI tip darajasida holatning mosligini kafolatlaydi — loading=true va user=null bir vaqtda olish mumkin emas. MVVM dasturchining intizomiga tayanadi.

Android uchun qanday MVI kutubxonalari mavjud?

Asosiy: MVIKotlin (Arkadii Ivanov, 2,5K yulduz, Kotlin Multiplatform), Orbit MVI (BabyJ, 1,3K yulduz), Mobius (Spotify, Kotlin/Java). MVIKotlin — Kotlin uchun eng mashhur, Orbit — o'rganish uchun eng sodda. Uchalasi ham sinovdan o'tkaziladigan Reducer va Middleware ni qo'llab-quvvatlaydi. Jetpack Compose uchun sealed State + Reducer orqali kutubxonasiz oddiy MVI yozish kifoya.

MVI uchun alohida kutubxona kerakmi?

Yo'q — sealed Intent + sealed State + ViewModel + StateFlow bog'liqliklarsiz ishlaydigan MVI beradi. Kutubxonalar (MVIKotlin, Orbit, TCA) Middleware, yon ta'sirlarni sinovdan o'tkazish va DI bilan integratsiyani qo'shadi. Oddiy loyihalar uchun kutubxona og'irligi asossizdir. 20+ ekranli murakkab loyihalar uchun kutubxona effektlarni tizimli qayta ishlash bilan o'zini oqlaydi.

MVI iOS uchun mosmi yoki bu Android patternimi?

MVI iOS uchun TCA (The Composable Architecture) orqali juda mos keladi — SwiftUI hamjamiyatining eng mashhur arxitekturasi. TCA aslida MVI + Redux + Combine. iOS da MVI ni TCA holda ObservableObject va sof reducer funksiyasi orqali ham amalga oshirish mumkin. SwiftUI o'zgarmas State bilan MVI siklga mukammal mos keladi.

MVI ni qanday sinovdan o'tkazish kerak?

Reducer unit testlar bilan sof funksiya sifatida sinovdan o'tkaziladi: boshlang'ich State beriladi, Intent jo'natiladi, yakuniy State tekshiriladi. Middleware mock-repozitori bilan sinovdan o'tkaziladi: LoadUser dan keyin getUser chaqirilgani tekshiriladi. ViewModel test: Intent jo'natish, StateFlow ni tekshirish. MVI MVVM dan osonroq sinovdan o'tkaziladi, chunki Reducer yashirin bog'liqliklarsiz sof funksiyadir.

Xulosa

  • MVI (Model-View-Intent) — bir tomonlama oqim va yagona holat bilan reaktiv pattern
  • Sealed State — tip darajasida moslikni kafolatlaydi, haqiqiy bo'lmagan kombinatsiyalarni istisno qiladi
  • Reducer — State + Intent → State sof funksiyasi, mock-obyektlarsiz sinovdan o'tkaziladi
  • Middleware — yon ta'sirlar (tarmoq, MB, navigatsiya) uchun alohida qatlam
  • MVI vs MVVM — MVI qat'iyroq va oldindan bashorat qilinadi, MVVM sodda va tezroq
  • Android — murakkab ekranlar uchun MVIKotlin yoki Orbit; oddiy ekranlar uchun MVVM
  • iOS — TCA (The Composable Architecture) — SwiftUI da MVI standarti

Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz

IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.

Loyihani muhokama qilish

Shuningdek o'qing