Unidirectional Data Flow — bu nədir, UDF Android və iOS-da

Müəllif: IT Sectr Dərc olunub: 2026-02-20 Oxuma vaxtı: 13 dəq

Unidirectional Data Flow — birtərəfli məlumat axını, məlumatların əks əlaqə olmadan State → View → Intent → Reducer → State qapalı dövrü boyunca hərəkət etdiyi arxitektura nümunəsidir. İkitərəfli bağlamadan fərqli olaraq, UDF vəziyyət dəyişikliyinin yalnız aşkar hərəkətlər (Intent/Event) vasitəsilə baş verməsini təmin edir, bu da məlumat axınını proqnozlaşdırıla bilən və izlənilən edir. Google I/O 2024 məlumatına görə, UDF orta və yüksək mürəkkəblikli biznes məntiqi olan Jetpack Compose və SwiftUI tətbiqləri üçün tövsiyə olunan arxitekturadır.

Əsas məqamlar

  • Unidirectional Data Flow (UDF) — vəziyyətin ciddi şəkildə dövr üzrə dəyişdiyi arxitektura nümunəsi: istifadəçi girişi → Intent → Reducer → yeni State → View-in yenidən çəkilməsi.
  • Android-də UDF ViewModel + StateFlow + Intent emalı vasitəsilə həyata keçirilir; iOS-da — @Observable + Reducer nümunəsi (Composable Architecture) ilə.
  • Google 2023-cü il sənədlərindən başlayaraq UDF-i Jetpack Compose üçün əsas arxitektura kimi tövsiyə edir.
  • UDF Tək Həqiqət Mənbəyi (Single Source of Truth) sayəsində İkitərəfli Bağlamanın sonsuz dövrü problemini aradan qaldırır.
  • Əsas çatışmazlıq — ikitərəfli bağlama ilə müqayisədə daha çox şablon kodu (State, Intent, Reducer, Effect).

Unidirectional Data Flow nədir?

Unidirectional Data Flow (UDF) — məlumatların qapalı dövr boyunca bir istiqamətdə hərəkət etdiyi, View və Model arasında əks əlaqəni istisna edən arxitektura nümunəsidir. UI-də dəyişikliyin dərhal modeli yenilədiyi İkitərəfli Bağlamadan fərqli olaraq, UDF hər vəziyyət dəyişikliyi üçün aşkar hərəkət (Intent, Event, Action) tələb edir. Bu, məlumat axınını tamamilə proqnozlaşdırıla bilən edir: istənilən anda hansı hərəkətin cari vəziyyətə gətirib çıxardığını müəyyən etmək olar.

UDF konsepsiyası veb-freymvorklardan — Redux (JavaScript, 2015) və Elm (2012) — gəlmiş və mobil inkişaf üçün uyğunlaşdırılmışdır. Google I/O 2024 məlumatına görə, UDF Jetpack Compose üçün tövsiyə olunan arxitekturaya çevrilmiş, klassik MVVM-i LiveData ilə əvəz etmişdir. iOS-da oxşar yanaşma Point-Free şirkətinin The Composable Architecture (TCA) ilə həyata keçirilir və Swift Community (2024) sorğusuna görə iOS tərtibatçılarının 15%-dən çoxu bundan istifadə edir.

UDF-nin əsas üstünlüyü — Single Source of Truth (SSOT): tətbiqin bütün vəziyyəti bir yerdə saxlanılır və ciddi müəyyən edilmiş əməliyyatlar vasitəsilə dəyişdirilir. Bu, debug, test və səhvlərin bərpasını asanlaşdırır, çünki hər vəziyyət dəyişikliyi qeydə alınır və eyni Intent-ləri təkrar göndərməklə bərpa edilə bilər.

UDF necə işləyir: State → View → Intent → Reducer dövrü

UDF-nin əsas dövrü dörd addımdan ibarətdir: State (cari vəziyyət) View-də göstərilir; istifadəçi hərəkət edir, bu da Intent-ə (niyyət) çevrilir; Intent Reducer-də (təmiz funksiya) işlənir və yeni State yaradır; yeni vəziyyət yenidən çəkilmək üçün View-ə ötürülür. Bu dövr hər istifadəçi və ya sistem hadisəsində təkrarlanır.

Dövrün hər bir elementinin ciddi məsuliyyəti var: State — ekranın müəyyən andakı vəziyyətini təsvir edən dəyişməz (immutable) obyekt; View — State-i göstərən funksiya; Intent — istifadəçinin niyyətini təsvir edən dəyər (məsələn, LoginIntent.Submit); Reducer — əlavə təsirləri olmayan, cari State və Intent-i qəbul edib yeni State qaytaran təmiz funksiya. Əlavə təsirlər (şəbəkə sorğuları, verilənlər bazası) ayrıca Middleware və ya Effect qatına çıxarılır.

Google Android Architecture (2024) məqaləsinə görə, Reducer-in təmizliyi əsas tələbdir: əgər Reducer şəbəkə çağırışı və ya verilənlər bazasına yazma ehtiva edirsə, məlumat axınını test etmək və debug etmək mümkün deyil. Bütün əlavə təsirlər Reducer çağırılmazdan əvvəl ViewModel korutini və ya Swift Task-da yerinə yetirilməli, nəticə isə yeni Intent kimi göndərilməlidir.

Android-də UDF: ViewModel + StateFlow + Intent

Android-də UDF tətbiqi üç Jetpack komponentinə əsaslanır: ViewModel həyat dövrünü idarə edir, StateFlow reaktiv vəziyyət axınını təmin edir, Intent (sealed class) bütün mümkün istifadəçi hərəkətlərini təsvir edir. View StateFlow-u Compose-da collectAsState() və ya View sistemində observe() vasitəsilə abunə edir.

Kotlin
sealed class LoginIntent {
    data object Submit : LoginIntent()
    data class UpdateEmail(val value: String) : LoginIntent()
    data class UpdatePassword(val value: String) : LoginIntent()
}

data class LoginState(
    val email: String = "",
    val password: String = "",
    val isLoading: Boolean = false,
    val error: String? = null
)

class LoginViewModel : ViewModel() {
    private val _state = MutableStateFlow(LoginState())
    val state: StateFlow<LoginState> = _state.asStateFlow()

    fun onIntent(intent: LoginIntent) {
        when (intent) {
            is LoginIntent.UpdateEmail -> {
                _state.update { it.copy(email = intent.value) }
            }
            is LoginIntent.UpdatePassword -> {
                _state.update { it.copy(password = intent.value) }
            }
            is LoginIntent.Submit -> {
                _state.update { it.copy(isLoading = true, error = null) }
                loginUseCase(_state.value.email, _state.value.password)
                    .onSuccess {
                        _state.update { it.copy(isLoading = false) }
                    }
                    .onFailure { e ->
                        _state.update { it.copy(isLoading = false, error = e.message) }
                    }
            }
        }
    }
}

@Composable
fun LoginScreen(viewModel: LoginViewModel) {
    val state by viewModel.state.collectAsState()

    LoginForm(
        email = state.email,
        onEmailChange = { viewModel.onIntent(LoginIntent.UpdateEmail(it)) },
        password = state.password,
        onPasswordChange = { viewModel.onIntent(LoginIntent.UpdatePassword(it)) },
        isLoading = state.isLoading,
        onSubmit = { viewModel.onIntent(LoginIntent.Submit) }
    )
}

Nümunə Android-də tam UDF dövrünü göstərir: LoginIntent bütün mümkün hərəkətləri təsvir edir (email dəyişmə, şifrə dəyişmə, formanın göndərilməsi), LoginState — dəyişməz vəziyyət, LoginViewModel Intent-ləri emal edir və StateFlow-u yeniləyir, Compose ekranı isə state-i collectAsState() vasitəsilə abunə edir. Hər vəziyyət dəyişikliyi müəyyən bir Intent-in emalının nəticəsidir və bu, məlumat axınını tamamilə şəffaf edir.

iOS-da UDF: TCA və Observable nümunəsi

iOS-da UDF The Composable Architecture (TCA) Point-Free və ya iOS 17+-dan etibarən yerli Observable nümunəsi vasitəsilə həyata keçirilir. TCA hazır State + Action + Reducer + Store dövrü təmin edir, burada Store yeganə həqiqət mənbəyidir, View isə dəyişiklikləri @Observable və ya ObservableObject vasitəsilə abunə edir.

Swift
struct LoginState: Equatable {
    var email = ""
    var password = ""
    var isLoading = false
    var error: String?
}

enum LoginAction {
    case emailChanged(String)
    case passwordChanged(String)
    case submit
    case loginResponse(Result<User, Error>)
}

let loginReducer = Reducer<LoginState, LoginAction> { state, action in
    switch action {
    case .emailChanged(let email):
        state.email = email
        return .none
    case .passwordChanged(let password):
        state.password = password
        return .none
    case .submit:
        state.isLoading = true
        state.error = nil
        return .run { send in
            let result = await loginUseCase(state.email, state.password)
            await send(.loginResponse(result))
        }
    case .loginResponse(.success):
        state.isLoading = false
        return .none
    case .loginResponse(.failure(let error)):
        state.isLoading = false
        state.error = error.localizedDescription
        return .none
    }
}

struct LoginView: View {
    let store: StoreOf<LoginReducer>

    var body: some View {
        WithViewStore(store, observe: { $0 }) { viewStore in
            Form {
                TextField("Email", text: viewStore.binding(get: \.email, send: { .emailChanged($0) }))
                SecureField("Password", text: viewStore.binding(get: \.password, send: { .passwordChanged($0) }))
                Button("Daxil ol") { viewStore.send(.submit) }
            }
        }
    }
}

loginReducer reduktoru — təmiz funksiyadır: şəbəkə sorğularını birbaşa yerinə yetirmir, əvəzində TCA mühiti tərəfindən icra ediləcək Effect qaytarır. Bu, reduktoru təcrid olunmuş şəkildə test etməyə, testlərdə effektləri əvəz etməyə imkan verir. View WithViewStore vasitəsilə Store dəyişikliklərinə abunə olur və send() vasitəsilə Action göndərir. TCA Store məhv edildikdə effektlərin ləğvini avtomatik idarə edir, yaddaş sızmalarının qarşısını alır.

UDF və MVVM: fərq nədir?

MVVMUDF tez-tez qarışdırılır, lakin aralarında prinsipial fərq var. MVVM — struktural nümunədir, kodu üç qata (Model, View, ViewModel) ayırır, lakin məlumat axınının istiqamətini müəyyən etmir. UDF — davranış nümunəsidir, məlumatların bu struktur daxilində necə hərəkət etdiyini təsvir edir. LiveData ilə MVVM-də həm ikitərəfli bağlama, həm də birtərəfli axın ola bilər — UDF MVVM-ə Intent emalı üçün ciddi qaydalar əlavə edir.

Android Developers (2024) sənədlərinə görə, Compose üçün tövsiyə olunan arxitektura MVVM daxilində UDF-dir: ViewModel State saxlayır və Intent-ləri emal edir, View State-ə abunə olur və Intent göndərir. DataBinding vasitəsilə İkitərəfli Bağlama ilə klassik MVVM Google tərəfindən yalnız biznes məntiqi olmayan sadə ekranlar üçün tövsiyə olunur. Jetpack Compose üçün əsas ssenari aşkar hadisə emalı ilə UDF-dir.

Müqayisə cədvəli:

XüsusiyyətMVVM (klassik)MVVM + UDF
Məlumat axınıMüəyyən edilməyibCiddi birtərəfli
Vəziyyət dəyişikliyiBirbaşa setText() iləYalnız Intent → Reducer vasitəsilə
Single Source of TruthXeyrBəli
Reducer-in test edilə bilməsiAşağıYüksək (təmiz funksiya)
Google tövsiyəsiKöhnəlmiş yanaşmaCompose üçün əsas

UDF tətbiqində tipik səhvlər

Ən geniş yayılmış səhv — Reducer daxilində əlavə təsirlər. MVVM-ə öyrəşmiş tərtibatçılar şəbəkə sorğularını birbaşa Intent handler-inə yerləşdirir, bu da Reducer-i qeyri-təmiz funksiyaya çevirir və test edilə bilməni pozur. Bütün effektlər dəyər (Effect / SideEffect) kimi qaytarılmalı və freymvork infrastrukturu tərəfindən icra edilməlidir. Android-də bunun üçün ViewModel-də korutinlər, TCA-da isə Effect.run istifadə olunur.

İkinci səhv — çox detallaşdırılmış Intent. Hər düymə basması, sürüşdürmə və mətn dəyişikliyi ayrıca Intent yaradır. Giriş sahələri üçün bu həddindən artıqdır — belə hallarda formanın daxilində birtərəfli axın ilə Binding istifadə etmək (lokal vəziyyət), qlobal Intent-i isə yalnız əhəmiyyətli hərəkətlərdə (göndərmə, naviqasiya) göndərmək məqbuldur.

Üçüncü səhv — effektlərin ləğvinin olmaması. İstifadəçi ekranı tərk edərsə, korutin və ya Task işləməyə davam edərsə, nəticə artıq məhv edilmiş View-ə tətbiq edilə bilər. Android-də viewModelScope.cancel() və ya takeWhileActive() istifadə edin; TCA-da effektlər Store məhv edildikdə avtomatik ləğv edilir. Google Issue Tracker (2024) məlumatına görə, tamamlanmamış korutinlərdən sızmalar Compose tətbiqlərinin çökmə səbəblərinin top-5-ə daxildir.

Tez-tez verilən suallar

UDF MVI-dan nə ilə fərqlənir?

MVI (Model-View-Intent) — üç məcburi elementi olan UDF-nin xüsusi halıdır: Intent (niyyət), Model (vəziyyət), View (göstərim). Əsas fərq ondadır ki, MVI-də ekranın hər bir vəziyyəti bir dəyişməz struktur (Sealed class) ilə təsvir edilir, View isə Model-dən UI-ə təmiz funksiyadır. UDF — Redux və Elm də daxil olmaqla hər hansı birtərəfli axını təsvir edən daha geniş termindir. Google sənədlərində UDF termini ümumi ad kimi, MVI isə konkret tətbiq kimi istifadə olunur.

UDF nə vaxt lazımsızdır?

UDF validasiyasız bir giriş sahəsi olan ekranlar, statik səhifələr və placeholder ekranlar üçün lazımsızdır. Ekranın biznes məntiqi yoxdursa və vəziyyəti istifadəçi hərəkətlərindən asılı deyilsə, UDF faydasız kod əlavə edir. Belə ssenarilər üçün birtərəfli bağlama və ya SwiftUI-də sadə @State kifayətdir. UDF ekranın mümkün vəziyyətlərinin sayı 3–4-ü keçdikdə və/yaxud əlavə təsirlər olduqda əsaslandırılır.

UDF-i necə test etmək olar?

Reducer təmiz funksiya olduğundan, onun test edilməsi State və Intent-in müxtəlif kombinasiyaları ilə çağırmağa və nəticədəki State və Effect-i yoxlamağa gəlir. Android-də StateFlow-u test etmək üçün Turbine istifadə edin: Intent göndərin, növbəti State emissiyasını yoxlayın. TCA-da daxili TestStore var ki, Action-dan sonra yalnız gözlənilən State sahələrinin dəyişdiyini və yalnız gözlənilən Effect-lərin icra edildiyini avtomatik yoxlayır.

UDF və Two-Way Binding birləşdirilə bilərmi?

Bəli, birləşdirmək mümkündür və çox vaxt optimaldır. Forma daxilində giriş sahələri üçün hər düymə basmasına Intent yaratmamaq üçün lokal Two-Way Binding (və ya SwiftUI-də Binding) istifadə edin. Forma göndərilərkən Reducer tərəfindən emal ediləcək toplanmış məlumatlarla bir Intent göndərin. Bu hibrid yanaşma — lokal Two-Way Binding ilə qlobal UDF — kommersiya SwiftUI tətbiqlərinin 70%-də istifadə olunur (Swift Community Survey 2024).

UDF, Redux və Elm-in ortaq cəhəti nədir?

Hər üç nümunə tək həqiqət mənbəyi ilə birtərəfli məlumat axını həyata keçirir. Elm (2012) — təmiz Model → View → Update dövrünü ilk təqdim edən funksional dildir. Redux (2015) Elm-i Store, Reducer və Action konsepti ilə JavaScript-ə uyğunlaşdırdı. UDF bu ideyaların mobil inkişaf üçün ümumiləşdirilməsidir. Hər üç yanaşma vəziyyətin bölünməz (atomik) yeniləmələri vasitəsilə proqnozlaşdırıla bilən dəyişiklikləri təmin edir.

Nəticə

  • Unidirectional Data Flow (UDF) — State → View → Intent → Reducer birtərəfli dövrü ilə vəziyyət dəyişikliklərinin proqnozlaşdırıla bilməsini təmin edən nümunədir.
  • Android-də UDF ViewModel + StateFlow + sealed class Intent vasitəsilə, iOS-da isə TCA (Reducer + Store) və ya yerli Observable ilə həyata keçirilir.
  • Google 2023-cü ildən başlayaraq UDF-i Jetpack Compose üçün əsas arxitektura kimi tövsiyə edir.
  • Reducer — əlavə təsirləri olmayan təmiz funksiya; bütün şəbəkə sorğuları və verilənlər bazası əməliyyatları Effect qatına çıxarılır.
  • UDF Intent-lərin aşkar emalı və Single Source of Truth sayəsində İkitərəfli Bağlamanın sonsuz dövrü problemini aradan qaldırır.
  • Əsas risklər — Reducer-də əlavə təsirlər, həddindən artıq detallaşdırılmış Intent və tamamlanmamış korutinlər.
  • Hibrid yanaşma (formada lokal Two-Way Binding + qlobal UDF) əksər kommersiya tətbiqləri üçün optimaldır.

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun