Какие слои используются в архитектуре iOS-приложения и как они взаимодействуют?

«Какие слои используются в архитектуре iOS-приложения и как они взаимодействуют?» — вопрос из категории Архитектура, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В современной iOS-разработке распространены многослойные архитектуры (MVVM, Clean Architecture, VIPER). Основные слои и их взаимодействие:

1. Presentation Layer (UI)

  • Состав: ViewControllers, Views, ViewModels/Presenters.
  • Задача: Отображение данных и обработка пользовательских действий.
  • Зависимости: Получает данные от Business Layer, передает пользовательские события вниз.

2. Business Logic Layer (Domain)

  • Состав: Use Cases (Interactors), бизнес-модели.
  • Задача: Инкапсуляция бизнес-правил, валидация, координация потока данных.
  • Ключевой принцип: Не зависит от фреймворков UI и Data слоев.

3. Data Layer

  • Состав: Репозитории (Repositories), Data Sources (локальные, сетевые).
  • Задача: Абстракция над источниками данных (сеть, база данных, кеш).
  • Реализация: Может использовать Core Data, URLSession, UserDefaults.

Взаимодействие: Слои общаются через протоколы (интерфейсы). Типичный поток:

  1. View сообщает ViewModel о действии пользователя.
  2. ViewModel вызывает Use Case.
  3. Use Case запрашивает данные у Repository.
  4. Repository возвращает данные из сети или локального хранилища.
  5. Данные проходят через Use Case (где применяются бизнес-правила) к ViewModel.
  6. ViewModel форматирует данные для отображения и обновляет View.

Пример абстракции через протокол:

// Data Layer Protocol
protocol UserRepositoryProtocol {
    func fetchUser(by id: String) async throws -> User
}

// Business Layer Use Case
class FetchUserProfileUseCase {
    private let repository: UserRepositoryProtocol
    init(repository: UserRepositoryProtocol) { self.repository = repository }

    func execute(userId: String) async throws -> UserProfile {
        let user = try await repository.fetchUser(by: userId)
        // Применяем бизнес-логику (например, валидацию, преобразование)
        return UserProfile(from: user)
    }
}

Преимущества: Тестируемость, гибкость, разделение ответственности, упрощенная поддержка.