← Назад к вопросам

Как происходит движение данных в VIPER?

1.7 Middle🔥 121 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Движение данных в VIPER-архитектуре

В VIPER (View-Interactor-Presenter-Entity-Router) движение данных происходит по строго определенным односторонним потокам, что обеспечивает четкое разделение ответственности и тестируемость. Архитектура следует принципу Single Responsibility Principle, где каждый компонент выполняет одну конкретную задачу.

Основной поток данных (пользовательское взаимодействие)

  1. Пользователь взаимодействует с View (например, нажимает кнопку).
  2. View делегирует обработку события Presenterу, не обрабатывая логику самостоятельно.
  3. Presenter запрашивает данные у Interactor, формируя бизнес-запрос.
  4. Interactor извлекает и обрабатывает данные, используя Entity и сторонние сервисы.
  5. Interactor возвращает результат Presenterу в удобном формате.
  6. Presenter подготавливает данные для отображения и передает их View.
  7. View отображает данные, обновляя UI.

Пример кода на Swift

Рассмотрим сценарий загрузки списка пользователей:

// 1. View (ViewController) обнаруживает событие
class UserViewController: UIViewController {
    var presenter: UserPresenterProtocol!
    
    @IBAction func loadUsersButtonTapped() {
        presenter.didRequestLoadUsers() // 2. Делегирование Presenterу
    }
    
    // 7. Обновление UI
    func displayUsers(_ users: [UserViewModel]) {
        // Обновление таблицы/коллекции
    }
}

// 3. Presenter обрабатывает запрос
class UserPresenter: UserPresenterProtocol {
    var interactor: UserInteractorInputProtocol!
    var view: UserViewProtocol!
    
    func didRequestLoadUsers() {
        interactor.fetchUsers() // 4. Запрос к Interactor
    }
}

// 4-5. Interactor выполняет бизнес-логику
class UserInteractor: UserInteractorInputProtocol {
    var presenter: UserInteractorOutputProtocol!
    var networkService: NetworkServiceProtocol!
    
    func fetchUsers() {
        networkService.getUsers { [weak self] result in
            switch result {
            case .success(let users):
                let processedUsers = users.map { UserEntity(id: $0.id, name: $0.name) }
                self?.presenter.didFetchUsers(processedUsers) // 5. Возврат результата
            case .failure(let error):
                self?.presenter.didFailFetchUsers(error)
            }
        }
    }
}

// 6. Presenter преобразует данные для View
extension UserPresenter: UserInteractorOutputProtocol {
    func didFetchUsers(_ users: [UserEntity]) {
        let viewModels = users.map { UserViewModel(name: $0.name) }
        view.displayUsers(viewModels) // 6. Передача подготовленных данных
    }
}

Ключевые особенности движения данных:

  • Односторонний поток: Данные движутся в одном направлении: View → Presenter → Interactor → Presenter → View. Это предотвращает циклические зависимости.
  • Инкапсуляция бизнес-логики: Вся сложная логика содержится в Interactor, который не зависит от фреймворков UI.
  • Пассивная View: View только отображает данные и передает события, не содержа логики.
  • Протоколы для коммуникации: Все взаимодействия определяются через протоколы, что обеспечивает:
    • Легкое тестирование (возможность подмены mock-объектов)
    • Слабую связность компонентов
    • Четкие контракты между модулями

Дополнительные потоки данных:

  • Навигация: Когда требуется переход на другой экран, Presenter обращается к Router, который инкапсулирует логику навигации:
presenter.didSelectUser(userId) {
    router.presentUserDetail(for: userId)
}
  • Обработка ошибок: Interactor передает ошибки Presenterу, который решает, как их показать пользователю (через View).

Преимущества такого подхода:

  1. Тестируемость: Каждый компонент тестируется изолированно.
  2. Масштабируемость: Легко добавлять новую функциональность.
  3. Поддержка многопоточности: Interactor может выполнять операции в фоновых потоках, возвращая результаты в главный.
  4. Чистота кода: Каждый разработчик понимает, где искать конкретную логику.

Движение данных в VIPER напоминает паттерн "Command Query Responsibility Segregation", где запросы на чтение и запись разделены, что делает архитектуру предсказуемой и устойчивой к изменениям требований.

Как происходит движение данных в VIPER? | PrepBro