Tek Yönlü Veri Akışı — Nedir, Android ve iOS'ta UDF

Yazar: IT Sectr Yayınlanma: 2026-02-20 Okuma süresi: 13 dk

Tek Yönlü Veri Akışı'nın ne olduğunu anlayın — tek yönlü bir veri akışı, verilerin geri bildirim döngüleri olmadan kapalı bir döngü State → View → Intent → Reducer → State içinde hareket ettiği bir mimari desen. İki yönlü bağlamanın aksine, UDF durum değişikliklerinin yalnızca açık eylemler (Intent/Event) aracılığıyla gerçekleşmesini garanti eder, bu da veri akışını öngörülebilir ve izlenebilir kılar. Google I/O 2024'e göre UDF, orta ve yüksek karmaşıklıkta iş mantığına sahip Jetpack Compose ve SwiftUI uygulamaları için önerilen mimaridir.

Önemli Noktalar

  • Tek Yönlü Veri Akışı (UDF) — durumun kesinlikle şu döngüyü izleyerek değiştiği bir mimari desen: kullanıcı girişi → Intent → Reducer → yeni State → View yeniden oluşturma.
  • Android'de UDF, ViewModel + StateFlow + Intent işleme yoluyla uygulanır; iOS'ta — @Observable + Reducer deseni (Composable Architecture) yoluyla.
  • Google, 2023 belgelerinden itibaren Jetpack Compose için birincil mimari olarak UDF'yi önermektedir.
  • UDF, Tek Bir Gerçek Kaynağı (Single Source of Truth) aracılığıyla İki Yönlü Bağlama'nın sonsuz döngü sorununu ortadan kaldırır.
  • Ana dezavantaj, iki yönlü bağlamaya kıyasla daha fazla kalıp koddur (State, Intent, Reducer, Effect).

Tek Yönlü Veri Akışı Nedir?

Tek Yönlü Veri Akışı (UDF), verilerin kapalı bir döngüde tek yönde hareket ettiği ve View ile Model arasındaki geri bildirim döngülerini ortadan kaldıran bir mimari desendir. UI'daki bir değişikliğin modeli hemen güncellediği İki Yönlü Bağlama'nın aksine, UDF her durum değişikliği için açık bir eylem (Intent, Event, Action) gerektirir. Bu, veri akışını tamamen öngörülebilir kılar: herhangi bir zamanda, hangi eylemin mevcut duruma yol açtığı belirlenebilir.

UDF kavramı web çerçevelerinden — Redux (JavaScript, 2015) ve Elm (2012) — gelmiştir ve mobil geliştirme için uyarlanmıştır. Google I/O 2024'e göre UDF, LiveData ile klasik MVVM'nin yerini alarak Jetpack Compose için önerilen mimari haline gelmiştir. iOS'ta, Point-Free tarafından The Composable Architecture (TCA)'da benzer bir yaklaşım uygulanmıştır ve Swift Community Survey (2024)'e göre iOS geliştiricilerinin %15'inden fazlası tarafından kullanılmaktadır.

UDF'nin ana avantajı Tek Bir Gerçek Kaynağıdır (SSOT): tüm uygulama durumu tek bir yerde depolanır ve kesin olarak tanımlanmış işlemlerle değiştirilir. Bu, hata ayıklamayı, test etmeyi ve hata yeniden üretimini basitleştirir, çünkü her durum değişikliği günlüğe kaydedilir ve aynı Intent'ler yeniden gönderilerek yeniden üretilebilir.

UDF Nasıl Çalışır: State → View → Intent → Reducer Döngüsü

Temel UDF döngüsü dört adımdan oluşur: State (mevcut durum) View'da oluşturulur; kullanıcı bir eylem gerçekleştirir ve bu bir Intent haline gelir; Intent bir Reducer'da (saf fonksiyon) işlenir ve yeni bir State oluşturur; yeni durum yeniden oluşturma için View'a iletilir. Bu döngü her kullanıcı veya sistem olayında tekrarlanır.

Döngünün her bir öğesinin katı bir sorumluluğu vardır: State — belirli bir andaki ekran durumunu tanımlayan değişmez bir nesne; View — State'i oluşturan bir fonksiyon; Intent — kullanıcının niyetini tanımlayan bir değer (örneğin, LoginIntent.Submit); Reducer — yan etkileri olmayan, mevcut State ve Intent'i alan ve yeni bir State döndüren saf bir fonksiyon. Yan etkiler (ağ istekleri, veritabanı işlemleri) ayrı bir Middleware veya Effect katmanına taşınır.

Google Android Architecture makalesine (2024) göre, Reducer'ın saflığı önemli bir gerekliliktir: Bir Reducer bir ağ çağrısı veya veritabanı yazma işlemi içeriyorsa, veri akışını test etmek ve hata ayıklamak imkansız hale gelir. Tüm yan etkiler, Reducer'ı çağırmadan önce bir ViewModel coroutine'i veya Swift Task'ında yürütülmeli ve sonuç yeni bir Intent olarak gönderilmelidir.

Android'de UDF: ViewModel + StateFlow + Intent

Android'de UDF uygulaması üç Jetpack bileşeni üzerine kurulmuştur: ViewModel yaşam döngüsünü yönetir, StateFlow reaktif bir durum akışı sağlar, Intent (sealed class) olası tüm kullanıcı eylemlerini tanımlar. View, Compose'da collectAsState() veya View sisteminde observe() aracılığıyla StateFlow'a abone olur.

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) }
    )
}

Örnek, Android'deki tam UDF döngüsünü göstermektedir: LoginIntent olası tüm eylemleri (e-posta değişikliği, şifre değişikliği, form gönderimi) tanımlar, LoginState değişmez durumdur, LoginViewModel Intent'leri işler ve StateFlow'u günceller ve Compose ekranı collectAsState() aracılığıyla duruma abone olur. Her durum değişikliği, belirli bir Intent'in işlenmesinin sonucudur ve bu da veri akışını tamamen şeffaf hale getirir.

iOS'ta UDF: TCA ve Observable Deseni

iOS'ta UDF, The Composable Architecture (TCA) (Point-Free) veya iOS 17+ ile yerel Observable deseni aracılığıyla uygulanır. TCA, State + Action + Reducer + Store'un hazır bir döngüsünü sağlar; burada Store tek gerçek kaynağıdır ve View, @Observable veya ObservableObject aracılığıyla değişikliklere abone olur.

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("Giriş Yap") { viewStore.send(.submit) }
            }
        }
    }
}

loginReducer saf bir fonksiyondur: ağ isteklerini doğrudan gerçekleştirmez, bunun yerine TCA çalışma zamanı tarafından yürütülecek bir Effect döndürür. Bu, testlerde efektleri değiştirerek indirgeyicinin izole bir şekilde test edilmesine olanak tanır. View, WithViewStore aracılığıyla Store değişikliklerine abone olur ve send() aracılığıyla Actions gönderir. TCA, Store yok edildiğinde efektlerin iptalini otomatik olarak yöneterek bellek sızıntılarını önler.

UDF ve MVVM: Fark Nedir?

MVVM ve UDF sıklıkla karıştırılır, ancak aralarında temel bir fark vardır. MVVM, kodu üç katmana (Model, View, ViewModel) ayıran yapısal bir desendir ancak veri akışının yönünü tanımlamaz. UDF, verilerin bu yapı içinde nasıl hareket ettiğini tanımlayan bir davranış desenidir. LiveData ile MVVM'de hem iki yönlü bağlama hem de tek yönlü akış mümkündür — UDF, MVVM'ye katı Intent işleme kuralları ekler.

Android Developers belgelerine (2024) göre, Compose için önerilen mimari MVVM içinde UDF'dir: ViewModel State'i depolar ve Intent'leri işler, View State'e abone olur ve Intent'leri gönderir. Google, DataBinding aracılığıyla İki Yönlü Bağlama ile klasik MVVM'yi yalnızca iş mantığı olmayan basit ekranlar için önermektedir. Jetpack Compose için birincil senaryo, açık olay işleme ile UDF'dir.

Karşılaştırma tablosu:

ÖzellikMVVM (klasik)MVVM + UDF
Veri akışıTanımlı değilKesinlikle tek yönlü
Durum değişikliğiDoğrudan setText() ileYalnızca Intent → Reducer ile
Tek Bir Gerçek KaynağıHayırEvet
Reducer test edilebilirliğiDüşükYüksek (saf fonksiyon)
Google önerisiEski yaklaşımCompose için birincil

UDF Uygularken Yapılan Yaygın Hatalar

En yaygın hata Reducer içindeki yan etkilerdir. MVVM'ye alışkın geliştiriciler ağ isteklerini doğrudan Intent işleyicisine yerleştirir, bu da Reducer'ı saf olmayan hale getirir ve test edilebilirliği bozar. Tüm efektler bir değer (Effect / SideEffect) olarak döndürülmeli ve çerçeve altyapısı tarafından yürütülmelidir. Android'de bunun için ViewModel'de coroutine'ler kullanılır; TCA'da — Effect.run.

İkinci hata aşırı ayrıntılı Intent'lerdir. Her tuş vuruşu, kaydırıcı hareketi ve metin değişikliği ayrı bir Intent oluşturur. Giriş alanları için bu aşırıdır — bu gibi durumlarda, form içinde tek yönlü akışla Bağlama (yerel durum) kullanmak ve yalnızca önemli eylemler (gönderme, gezinme) için genel bir Intent göndermek kabul edilebilir.

Üçüncü hata efekt iptali işleme eksikliğidir. Kullanıcı ekrandan ayrıldığında bir coroutine veya Task hala yürütülüyorsa, sonuç zaten yok edilmiş bir View'a uygulanabilir. Android'de viewModelScope.cancel() veya takeWhileActive() kullanın; TCA'da efektler Store yok edildiğinde otomatik olarak iptal edilir. Google Issue Tracker (2024)'e göre, tamamlanmamış coroutine'lerden kaynaklanan sızıntılar Compose uygulamalarında ilk 5 çökme nedeni arasındadır.

Sıkça Sorulan Sorular

UDF, MVI'dan nasıl farklıdır?

MVI (Model-View-Intent), üç zorunlu öğeye sahip UDF'nin belirli bir durumudur: Intent (niyet), Model (durum), View (görüntüleme). Temel fark, MVI'da her ekran durumunun tek bir değişmez yapı (Sealed class) ile tanımlanması ve View'ın Model'den UI'ya saf bir fonksiyon olmasıdır. UDF, Redux ve Elm dahil olmak üzere herhangi bir tek yönlü akışı tanımlayan daha geniş bir terimdir. Google belgelerinde UDF terimi genel bir ad olarak kullanılırken, MVI belirli bir uygulamadır.

UDF ne zaman aşırıdır?

UDF, doğrulamasız tek bir giriş alanına sahip ekranlar, statik sayfalar ve yer tutucu ekranlar için aşırıdır. Bir ekranın iş mantığı yoksa ve durumu kullanıcı eylemlerine bağlı değilse, UDF fayda sağlamadan gereksiz kod ekler. Bu tür senaryolar için tek yönlü bağlama veya SwiftUI'de basit @State yeterlidir. UDF, olası ekran durumu sayısı 3-4'ü aştığında ve/veya yan etkiler mevcut olduğunda haklı çıkar.

UDF nasıl test edilir?

Reducer saf bir fonksiyon olduğundan, test etmek onu farklı State ve Intent kombinasyonlarıyla çağırmak ve sonuçta ortaya çıkan State ve Effect'i kontrol etmekten ibarettir. Android'de StateFlow'u test etmek için Turbine kullanın: bir Intent gönderin, sonraki State emisyonunu kontrol edin. TCA'da, bir Action'dan sonra yalnızca beklenen State alanlarının değiştiğini ve yalnızca beklenen Effect'lerin yürütüldüğünü otomatik olarak doğrulayan yerleşik bir TestStore vardır.

UDF ve İki Yönlü Bağlama birleştirilebilir mi?

Evet, bunları birleştirmek kabul edilebilir ve genellikle en uygunudur. Bir form içindeki giriş alanları için, her tuş vuruşunda bir Intent oluşturmaktan kaçınmak için yerel İki Yönlü Bağlama (veya SwiftUI'de Binding) kullanın. Form gönderiminde, toplanan verilerle tek bir Intent gönderin ve bu Reducer tarafından işlenir. Bu hibrit yaklaşım — yerel İki Yönlü Bağlama ile genel UDF — ticari SwiftUI uygulamalarının %70'inde kullanılır (Swift Community Survey 2024 verileri).

UDF, Redux ve Elm'in ortak noktası nedir?

Her üç desen de tek bir gerçek kaynağı ile tek yönlü veri akışını uygular. Elm (2012) — işlevsel bir dil — ilk olarak saf Model → View → Update döngüsünü tanıttı. Redux (2015), Store, Reducer ve Action kavramlarıyla Elm'i JavaScript'e uyarladı. UDF, bu fikirlerin mobil geliştirme için genelleştirilmesidir. Her üç yaklaşım da atomik durum güncellemeleri yoluyla değişikliklerin öngörülebilirliğini garanti eder.

Özet

  • Tek Yönlü Veri Akışı (UDF), öngörülebilir durum değişiklikleri sağlayan tek yönlü State → View → Intent → Reducer döngüsüne sahip bir desendir.
  • Android'de UDF, ViewModel + StateFlow + sealed class Intent ile uygulanır; iOS'ta — TCA (Reducer + Store) veya yerel Observable ile.
  • Google, 2023'ten itibaren Jetpack Compose için birincil mimari olarak UDF'yi önermektedir.
  • Reducer, yan etkileri olmayan saf bir fonksiyondur; tüm ağ istekleri ve veritabanı işlemleri Effect katmanına taşınır.
  • UDF, açık Intent işleme ve Tek Bir Gerçek Kaynağı aracılığıyla İki Yönlü Bağlama'nın sonsuz döngü sorununu ortadan kaldırır.
  • Ana riskler — Reducer'da yan etkiler, aşırı ayrıntılı Intent'ler ve tamamlanmamış coroutine'ler.
  • Hibrit yaklaşım (formlarda yerel İki Yönlü Bağlama + genel UDF) çoğu ticari uygulama için idealdir.

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun