Что такое однонаправленный поток данных (Unidirectional Data Flow) в архитектуре приложений?

«Что такое однонаправленный поток данных (Unidirectional Data Flow) в архитектуре приложений?» — вопрос из категории Архитектура, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Однонаправленный поток данных (Unidirectional Data Flow, UDF) — это архитектурный паттерн, при котором данные в приложении циркулируют в одном предсказуемом направлении: от источника состояния (State) к представлению (View) и обратно через строго определенные действия (Actions).

Ключевой принцип:

  1. Состояние (State) — единственный источник истины для UI.
  2. Представление (View) — отображает состояние и отправляет действия (Actions) при событиях от пользователя.
  3. Редьюсер (Reducer) — чистая функция, которая принимает текущее состояние и действие, возвращая новое состояние.

Пример на SwiftUI с архитектурой, похожей на Redux:

// 1. Состояние (State)
struct AppState {
    var counter: Int = 0
}

// 2. Действия (Actions)
enum AppAction {
    case increment
    case decrement
}

// 3. Редьюсер (Reducer)
func appReducer(state: inout AppState, action: AppAction) {
    switch action {
    case .increment:
        state.counter += 1
    case .decrement:
        state.counter -= 1
    }
}

// 4. Хранилище (Store) и Представление (View)
import SwiftUI
import Combine

final class Store: ObservableObject {
    @Published private(set) var state: AppState

    init(state: AppState = AppState()) {
        self.state = state
    }

    func send(_ action: AppAction) {
        appReducer(state: &state, action: action) // Данные обновляются здесь
    }
}

struct CounterView: View {
    @EnvironmentObject var store: Store

    var body: some View {
        VStack {
            Text("Счетчик: (store.state.counter)") // Данные читаются
            Button("+") { store.send(.increment) } // Действие отправляется
            Button("-") { store.send(.decrement) }
        }
    }
}

Преимущества:

  • Предсказуемость: Легко отследить, как и почему изменилось состояние.
  • Тестируемость: Редьюсеры — чистые функции, их легко тестировать изолированно.
  • Отладка: Централизованное состояние упрощает логирование и инструментирование (например, time-travel debugging).
  • Согласованность UI: Все представления синхронизированы с одним источником состояния.

Где используется: Redux (JavaScript), The Composable Architecture (TCA, Swift), Flux, а также является базовым принципом управления состоянием в SwiftUI через @State, @ObservableObject и @EnvironmentObject.