Услуги Портфолио Контакты БлогНовости +7 (800) 511‑32‑27 Обсудить проект
Mobile

Архитектура мобильного приложения: слои, компоненты и паттерны

13 августа 2026·20 мин чтения
Юрий Пухов
Автор материалаЮрий ПуховCEO, YuSMP Group
Профиль автора
Слои архитектуры мобильного приложения

По данным Statista, совокупно в Google Play и App Store представлено более 4,5 миллиона приложений — и конкуренция за место в топе только ужесточается. При этом многие проекты проигрывают не из-за плохих идей, а из-за скрытой причины — неудачной архитектуры мобильного приложения. Когда кодовая база превращается в запутанный монолит, каждый новый экран или фича обходится вдвое дороже, а обновления превращаются в минное поле. Именно поэтому Google в своём Guide to App Architecture и Apple в официальной документации закрепили архитектурные рекомендации на уровне платформенного стандарта: слои, однонаправленный поток данных и разделение ответственности закладываются до первой строки кода.

В этой статье я разберу архитектуру мобильного приложения по слоям и паттернам — без академического тумана, с фокусом на то, как это влияет на скорость выпуска фич, стоимость поддержки и устойчивость продукта к изменениям. Материал будет полезен продакт-менеджерам, CTO и предпринимателям, которые хотят говорить с разработчиками на одном языке и принимать обоснованные решения. Если вы уже знаете, что вам нужно мобильное приложение с продуманной архитектурой — смотрите услугу разработки мобильных приложений в YuSMP Group.

Коротко: Архитектура мобильного приложения — это три слоя (Presentation / Domain / Data) и паттерн их взаимодействия. MVVM — оптимальный старт для большинства проектов. Clean Architecture оправдана при командах от 5 человек и сроке жизни продукта от 2 лет. Правильный выбор с первого дня снижает стоимость поддержки в 2–3 раза.

Что такое архитектура мобильного приложения и зачем она нужна

Архитектура ≠ технологический стек: разделяем понятия

Когда я слышу вопрос «а на каком стеке у вас написано?», за ним нередко скрывается другой, более важный: «а как у вас внутри всё устроено?». Стек — это Kotlin, Swift или Flutter, Jetpack Compose или SwiftUI. Архитектура — это принципы организации кода: как разделены слои ответственности, как данные движутся между компонентами, как части системы общаются. Можно взять один и тот же Kotlin и реализовать на нём как чистое MVVM, так и то, что принято называть «архитектурой-спагетти».

Архитектура определяет три вещи, которые напрямую влияют на бизнес-показатели продукта:

  • Скорость выпуска фич. Когда логика размазана по всем слоям, добавить новый экран — значит переписать половину приложения. В хорошо структурированном коде экраны добавляются независимо.
  • Стоимость сопровождения. По нашему опыту, поддержка приложения без явной архитектуры обходится в 2–3 раза дороже: больше багов при каждом изменении, дольше онбординг новых разработчиков.
  • Технический долг. Отсутствие архитектурных принципов — это кредит, который рано или поздно придётся отдавать переработками или полным переписыванием продукта.

Что даёт продуманная архитектура бизнесу

Представьте два сценария. В первом — разработчик меняет источник данных с REST на GraphQL. При правильной архитектуре он трогает только data-слой: правит репозиторий, тесты проходят, PR закрыт за день. Во втором сценарии логика запросов разбросана по фрагментам и ViewModel-ах — изменения расползаются по десяткам файлов, и любая доработка несёт риск регрессии. Хорошая архитектура делает второй сценарий невозможным.

Слои архитектуры: presentation, domain, data

Современная рекомендация Google и де-факто стандарт в мобильной разработке — трёхслойная модель: Presentation → Domain → Data. Каждый слой знает только о слое ниже, но не знает о слое выше. Это и есть принцип разделения ответственности (separation of concerns).

Слой Ответственность Примеры компонентов
Presentation (UI) Отображение состояния, обработка ввода пользователя Activity, Fragment, Composable (Android); View, UIViewController, SwiftUI View (iOS); ViewModel, UiState
Domain (бизнес-логика) Бизнес-правила, use cases, доменные модели UseCase / Interactor, доменные Entity, валидация, агрегация данных
Data Получение и хранение данных, абстракция источников Repository, DataSource (Remote/Local), Room / Core Data, Retrofit / URLSession

Presentation (UI) — экраны, состояние, взаимодействие с пользователем

Задача UI-слоя — отобразить состояние и отдать пользовательские события вниз. Presentation-слой ничего не знает о том, откуда пришли данные: для него существует только ViewModel (или иной «посредник»), который поставляет готовый UiState. Это позволяет тестировать логику отображения отдельно от бизнес-правил и сетевого слоя.

Domain — бизнес-логика, use cases, независимость от фреймворков

Domain-слой — сердце приложения и единственный по-настоящему платформенно-независимый слой. Здесь живут use cases: «получить список заказов», «рассчитать корзину», «авторизовать пользователя». Use case берёт данные из репозитория, применяет бизнес-правила и возвращает результат в ViewModel. Если domain-слой написан без зависимостей от Android SDK или UIKit, его можно переиспользовать между нативным и кросс-платформенным приложением — или покрыть тестами без эмулятора.

Data — репозитории, источники данных, локальная БД и сеть

Data-слой инкапсулирует всю логику получения и хранения данных. Центральная абстракция — репозиторий: он принимает запрос от domain-слоя и сам решает, откуда брать данные — из кэша (Room/SQLite, Core Data), из сети (REST/GraphQL API) или из локального файла. Это позволяет менять источник данных, не затрагивая бизнес-логику и UI.

Принцип separation of concerns и однонаправленный поток данных (UDF)

Однонаправленный поток данных (Unidirectional Data Flow) означает: данные движутся сверху вниз (от ViewModel к UI), а события — снизу вверх (от UI к ViewModel). Никаких «обратных связей» через колбэки или прямых ссылок между экранами. Этот принцип заложен в основу Jetpack Compose, SwiftUI и всех современных мобильных архитектур. Благодаря UDF состояние приложения всегда предсказуемо и воспроизводимо — это упрощает отладку и тестирование.

Разрабатываем мобильные приложения под iOS и Android

Выберем архитектуру под ваш проект и соберём команду — оценка за 2 дня бесплатно.

Обсудить проект

Архитектурные паттерны: MVC, MVP, MVVM, MVI, Clean Architecture

MVC и MVP — классика и её ограничения на мобильных

MVC (Model-View-Controller) — исторически первый паттерн на мобильных, тесно связанный с UIKit и ранним Android. Его главная проблема: Controller (Activity/ViewController) быстро разрастается до «God Object», берёт на себя и навигацию, и бизнес-логику, и работу с данными — так появился знаменитый «Massive ViewController».

MVP (Model-View-Presenter) вынес логику в Presenter, отделив её от View. Это улучшило тестируемость, но породило другую проблему: жёсткую связку View и Presenter через интерфейсы. При изменении требований к UI интерфейс приходилось менять синхронно с Presenter, что увеличивало объём рутинного кода.

MVVM — доминирующий паттерн

MVVM (Model-View-ViewModel) сегодня — стандарт де-факто для Android (Jetpack ViewModel + StateFlow / LiveData) и iOS (SwiftUI + Observation framework / Combine). ViewModel хранит и преобразует состояние UI, не имея прямой ссылки на View. Связь между ними — через реактивный поток данных: View подписывается на UiState и обновляется автоматически при изменении состояния. Это даёт низкий порог входа, хорошую тестируемость ViewModel и естественную интеграцию с платформенными инструментами.

MVI и однонаправленный state

MVI (Model-View-Intent) развивает идеи MVVM дальше: весь UI-слой описывается как цикл Intent → State → View. Пользовательское действие создаёт Intent, он обрабатывается через Reducer, который меняет неизменяемое (immutable) состояние — и View рендерит новое состояние. MVI отлично масштабируется на сложный UI со многими источниками событий: помогает избежать состояний-гонки и делает поток событий аудируемым. На Android это реализуется через библиотеки вроде Orbit MVI или собственный Kotlin Flow + sealed class.

Clean Architecture / VIPER — когда оправдана дополнительная сложность

Clean Architecture по Роберту Мартину вводит явную зависимость только «внутрь»: presentation зависит от domain, domain — ни от чего. На iOS этот принцип реализуется через VIPER (View-Interactor-Presenter-Entity-Router). Добавляются явные границы через протоколы и инверсию зависимостей. Цена — значительный boilerplate и высокий порог входа. Оправдана в крупных приложениях с несколькими командами: четкие границы модулей позволяют командам работать параллельно без конфликтов.

Сравнительная таблица паттернов

Паттерн Суть Тестируемость Сложность / порог входа Когда применять
MVC Контроллер управляет логикой и UI Низкая (логика в контроллере) Низкая Прототипы, очень маленькие приложения
MVP Presenter отделён от View через интерфейс Средняя Средняя Небольшие Android-приложения без Jetpack
MVVM ViewModel хранит состояние; View реагирует на поток Высокая Низкая — средняя Большинство новых проектов, рекомендован Google и Apple
MVI Циклическая модель Intent → State → View Очень высокая Средняя — высокая Сложный UI, много источников событий, строгий аудит состояний
Clean / VIPER Строгие слоевые границы, инверсия зависимостей Очень высокая Высокая Большие команды, долгий жизненный цикл, сложная бизнес-логика

Сквозные компоненты архитектуры

Управление состоянием (state management)

Состояние — это то, что UI отображает в данный момент. Управлять им сложно: состояние может прийти с сервера, из базы данных, от пользователя или из нескольких мест одновременно. Современный подход — хранить единое неизменяемое UiState в ViewModel и обновлять его атомарно. На Android это StateFlow / MutableStateFlow; на iOS — @Observable (Observation framework в Swift 5.9+) или @Published + ObservableObject. Это исключает ситуации, когда разные части UI имеют несогласованное состояние.

Навигация

Навигация — точка сбора всех экранов приложения, и она часто становится узким местом архитектуры. Ошибка — держать логику навигации в Activity или Fragment. Правильный подход — выделить навигацию в отдельный компонент: NavController в Jetpack Navigation (Android) или NavigationStack в SwiftUI. В Clean Architecture / VIPER навигацию выносят в Router — отдельный объект, который обновляется ViewModel-ом через события.

Внедрение зависимостей (DI)

Без DI каждый компонент сам создаёт свои зависимости — это связывает код, делает его нетестируемым и усложняет подмену реализаций. Стандарт на Android — Hilt (обёртка над Dagger 2), которая интегрирована в Jetpack и покрывает жизненные циклы компонентов. На iOS используют Resolver, Swinject или ручной dependency container. Правильный DI позволяет в тестах передавать mock-зависимости, не меняя код продакшн-компонентов.

Работа с данными: offline-first, кэш, синхронизация

Пользователи ожидают, что приложение работает при плохом соединении или его отсутствии. Паттерн offline-first означает: источником истины для UI всегда является локальная база данных (Room / SQLite на Android, Core Data или GRDB на iOS). Репозиторий по запросу возвращает кэш немедленно, параллельно обновляя его из сети. Стратегии: cache-first (сначала кэш, потом сеть), network-first (сначала сеть, при сбое — кэш), stale-while-revalidate (кэш немедленно + тихое обновление). Для синхронизации фоновых изменений на Android используют WorkManager; на iOS — BGAppRefreshTask и URLSession background transfers.

Уровень офлайн Что доступно без сети Когда применять
0 — нет поддержки Ничего: белый экран или ошибка Прототипы и внутренние инструменты с гарантированным WiFi
1 — чтение кэша Последние загруженные данные (read-only) Новостные и медиа-приложения, каталоги с редким обновлением
2 — полный CRUD офлайн Создание, редактирование, удаление — синхронизация при восстановлении сети Продуктивность (заметки, задачи), CRM для полевых сотрудников
3 — multi-device sync Полный CRUD + разрешение конфликтов между устройствами Совместное редактирование, мобильный ERP, медицинские карты

Безопасность: хранение секретов, шифрование, авторизация

Безопасность в архитектуре — это не отдельная фича, а сквозное требование. Три ключевых принципа: никогда не хранить токены и чувствительные данные в SharedPreferences или UserDefaults в открытом виде — только в Android Keystore / iOS Keychain; шифровать локальную базу данных (SQLCipher для Room); авторизационные токены передавать исключительно через HTTPS, а логику обмена токенами изолировать в data-слое. Взаимодействие с бэкендом — это тоже часть data-слоя: разработка API должна проектироваться совместно с клиентским data-слоем, чтобы авторизация, версионирование и обработка ошибок были согласованы.

Работа с сетью: таймауты, повторы и обрывы соединения

Мобильная сеть ненадёжна: 4G обрывается в лифте, WiFi переключается между точками, пользователь теряет соединение в середине запроса. Надёжное приложение обрабатывает эти сценарии явно — на уровне архитектуры, а не как случайное исключение.

  • Timeout + retry с экспоненциальным отступом. Каждый сетевой запрос должен иметь лимит по времени (connect timeout, read timeout). При сбое — повторная попытка с нарастающим интервалом (1 с → 2 с → 4 с), чтобы не перегружать сервер в момент восстановления. На Android — OkHttp Interceptor; на iOS — URLSession retry policy.
  • Идемпотентность мутаций. POST-запросы (создание заказа, платёж) должны быть защищены от дублирования. Типичный паттерн — idempotency key: уникальный UUID в заголовке, который сервер использует для дедупликации. Без него повторная попытка после обрыва грозит двойным списанием или двойным заказом.
  • Офлайн-очередь для критичных действий. Если пользователь нажал «Сохранить» без сети — запрос должен попасть в локальную очередь и выполниться при восстановлении. На Android — WorkManager с Constraints.NETWORK_REQUIRED; на iOS — URLSession background task. Это логика data-слоя, не UI.

Синхронизация данных и разрешение конфликтов

Когда приложение работает офлайн и пользователь изменяет данные на нескольких устройствах, возникают конфликты. Это архитектурная задача, решать которую нужно при проектировании, а не после первого инцидента с потерей данных.

  • Last-write-wins (LWW). Последняя запись побеждает — простейший подход. Подходит для настроек профиля, предпочтений, параметров уведомлений. Не подходит для финансовых транзакций и совместного редактирования.
  • Server-wins. При синхронизации версия сервера всегда приоритетна. Разумный выбор для каталогов, прайс-листов и данных, которые изменяет бизнес, а не пользователь.
  • Merge / CRDT. Оба изменения объединяются по правилам (CRDT — Conflict-free Replicated Data Types). Применяется в совместных редакторах. Реализация сложна — оправдана только при реальной multi-user коллаборации.

Ключевой принцип: выбор стратегии должен быть зафиксирован на этапе проектирования data-слоя. Иначе конфликты разрешаются случайно — и пользователи теряют данные без видимой ошибки.

Архитектура и способ разработки: нативная vs кросс-платформа

Нативная разработка (Kotlin/Jetpack, Swift/SwiftUI)

Нативная разработка даёт полный доступ к платформенным API и максимальную производительность. На Android архитектурный стандарт — Jetpack: ViewModel, Room, Navigation, WorkManager. Google рекомендует MVVM с однонаправленным потоком и Jetpack Compose как UI-фреймворк. На iOS Apple движется к SwiftUI + Observation + Swift Concurrency (async/await) как современному стеку. Каждая платформа диктует свои паттерны, но принципы слоёв остаются едиными.

Flutter / React Native — как ложатся те же слои и паттерны

Кросс-платформенные фреймворки не отменяют необходимость архитектуры — они лишь меняют инструменты. В Flutter широко применяют BLoC (Business Logic Component) — аналог MVI — или Riverpod как state management. React Native использует Zustand, Redux Toolkit или MobX. Принципиальное преимущество кросс-платформы — единый domain-слой: бизнес-логика и use cases пишутся один раз и работают и на iOS, и на Android. Data-слой также может быть общим, хотя платформо-специфичные вещи (Keychain vs Keystore, уведомления) всё равно реализуются отдельно.

Влияние выбора на архитектуру data-слоя и переиспользование кода

При нативной разработке две команды (iOS и Android) реализуют репозитории независимо — дублирование логики неизбежно. При кросс-платформенном подходе domain и data-слои общие, что снижает риск расхождения поведения между платформами. Компромисс: нативные интеграции (камера, биометрия, push-уведомления) требуют platform-channel или native-module — здесь дублирования не избежать в любом случае.

Пошаговый процесс проектирования архитектуры

Хорошая архитектура — результат осознанного процесса, а не случайного набора решений. Вот пять шагов, которые мы используем в YuSMP Group при старте каждого мобильного проекта.

  1. Анализ требований до выбора паттерна. До того как называть MVVM или Clean Architecture — ответить на ключевые вопросы: сколько экранов и источников данных, нужен ли полноценный офлайн-режим, каковы требования к безопасности, насколько сложна бизнес-логика, как часто будут меняться требования к UI. Именно ответы диктуют архитектуру, а не наоборот.
  2. Выбор технологического стека. Нативная iOS/Android или кросс-платформа (Flutter, React Native)? Это решение определяет доступные инструменты: Jetpack ViewModel и Coroutines, BLoC и Provider, Redux и Zustand. Стек и архитектура выбираются совместно — некоторые паттерны плохо ложатся на определённые платформы.
  3. Выбор архитектурного паттерна. Для большинства новых проектов — MVVM. Для сложного UI с многими независимыми источниками событий — MVI. Для крупной команды и долгосрочного продукта — Clean Architecture. Матрица выбора в следующем разделе поможет принять решение системно.
  4. Определение модулей. Разбейте приложение на функциональные модули: auth, profile, catalog, checkout, notifications. На Android — multi-module Gradle project; на iOS — Swift Package Manager modules. Модульность позволяет командам работать параллельно без конфликтов и даёт компилятору кешировать части сборки независимо.
  5. Фиксация контрактов между слоями. До написания кода — зафиксировать интерфейсы: Repository interfaces, UseCase signatures, UiState models. Это точка согласования между командами и страховка от «дрейфа» архитектуры в процессе разработки.

Чек-лист до старта разработки:

  • Требования к офлайн-режиму определены и задокументированы
  • Источники данных (REST, GraphQL, WebSocket) перечислены
  • Репозиторные интерфейсы согласованы с командой бэкенда
  • Модульная структура утверждена до первого спринта
  • Стратегия DI выбрана (Hilt, Koin, Resolver, Swinject)
  • Тестируемость определена как требование, а не пожелание

Как выбрать архитектуру под свой проект

MVP и небольшие приложения: не переусложнять

Для приложений с 5–10 экранами, простым сценарием и командой из 1–2 разработчиков достаточно MVVM без domain-слоя: ViewModel напрямую вызывает репозиторий. Добавлять use cases, Router и DI-фреймворк на старте — значит тратить время на инфраструктуру вместо продукта. Архитектуру всегда можно усложнить потом — главное заложить правильные границы с первого дня.

Продукты со сложной логикой и долгим жизненным циклом: Clean Architecture

Если приложение — ключевой канал бизнеса, работает несколько лет и развивается несколькими командами, инвестиция в Clean Architecture окупается. Явные границы модулей позволяют командам работать параллельно, снижают время онбординга и делают код аудируемым. Для таких проектов рекомендую сразу закладывать multi-module architecture (Android) или Swift Package Manager modules (iOS) — это принудительно соблюдает границы слоёв на уровне компилятора.

Чек-лист принятия решения

  • Масштаб: сколько экранов, сколько источников данных, насколько сложна бизнес-логика?
  • Команда: сколько разработчиков, каков их опыт с конкретными паттернами?
  • Срок жизни: это MVP на 6 месяцев или продукт на 5 лет?
  • Тестируемость: насколько критичны автоматические тесты для вашего процесса?
  • Темп изменений: как часто будут меняться требования к UI и бизнес-правилам?

Если вы строите мобильный продукт впервые или переосмысливаете архитектуру существующего — наша команда занимается разработкой мобильных приложений под ключ и всегда начинает с аудита требований и выбора архитектурного подхода под задачу.

Матрица выбора: паттерн по масштабу проекта

Масштаб проекта Паттерн Domain-слой DI Модульность
MVP / прототип
1–2 разработчика, <10 экранов, до 6 мес.
MVVM Необязателен Manual / Koin Монорепо
Средний продукт
3–5 разработчиков, 10–30 экранов, 1–2 года
MVVM + чистый domain Обязателен Hilt / Resolver Feature-модули
Крупный продукт
5+ разработчиков, 30+ экранов, 3+ года
Clean Architecture / MVI Обязателен, покрыт тестами Hilt / Dagger Multi-module / SPM
Финтех / медтех
Любой размер, высокие требования к безопасности
Clean Architecture Обязателен, аудируемый Hilt / Dagger Multi-module

Как оценить архитектурную компетентность подрядчика

Если вы выбираете команду разработки, эти вопросы помогут понять уровень архитектурного мышления — без необходимости читать код:

  • Какой архитектурный паттерн вы используете и почему именно он подходит для моего проекта, а не другой?
  • Как организован DI — и что произойдёт, если нужно сменить DI-библиотеку?
  • Как тестируется domain-слой — без запуска эмулятора?
  • Как приложение ведёт себя при отсутствии сети: что кэшируется, как долго, что происходит при конфликте данных?
  • Как вы обрабатываете одновременные запросы с истёкшим токеном авторизации?
  • Сколько времени займёт онбординг нового разработчика через два года?

Смена команды: когда это возможно без переписывания

Одна из скрытых метрик качества архитектуры — насколько легко передать проект новой команде. Если логика размазана по Activity и Fragment, новая команда не может понять систему без долгого «раскапывания» кода. Добавление фичи становится рискованным угадыванием, а не предсказуемой задачей.

Архитектура с чёткими слоями и задокументированными интерфейсами делает передачу предсказуемой: новая команда изучает Repository interfaces, UseCase signatures и UiState models — это документация, которая не устаревает, потому что живёт прямо в коде. Онбординг занимает дни, а не месяцы.

Практический ориентир: если за неделю новый разработчик может добавить новый экран с полным циклом данных (UseCase → Repository → ViewModel → UI), архитектура успешно передаётся. Если на это уходит больше двух недель — признак скрытого технического долга.

Частые ошибки в архитектуре мобильного приложения

За годы работы с мобильными проектами я вижу одни и те же паттерны ошибок, которые потом приходится дорого исправлять:

  • Bulky ViewModel / God Object. ViewModel начинает тянуть на себя навигацию, форматирование строк, прямые запросы в БД. Итог — монолитный класс на 2000 строк, который невозможно тестировать.
  • Логика в UI. Бизнес-правила в Activity/Fragment или Composable — классика, которая убивает тестируемость. Любая логика, которую можно протестировать без запуска устройства, должна жить не в UI-слое.
  • Отсутствие слоёв. «Один большой класс, который делает всё» — Data Manager, App Controller. Когда нужно поменять источник данных, перекраивается весь код.
  • Преждевременная оптимизация. Закладывать микросервисную архитектуру в MVP с 500 пользователями — выброшенные деньги. Оптимизируйте то, что измерили как узкое место.
  • Жёсткая связанность с библиотекой. Когда весь код напрямую зависит от конкретного сетевого клиента или ORM — замена библиотеки становится рефакторингом всего приложения. Репозиторий с интерфейсом — страховка от смены инструментов.
  • Отсутствие тестируемости как критерия. «Мы напишем тесты потом» — это технический долг, который растёт быстрее, чем кажется. Хорошая архитектура делает тесты дешёвыми; плохая — делает их невозможными.

CI/CD и тестирование в контексте архитектуры

Хорошая архитектура не только структурирует код — она делает тестирование и непрерывную интеграцию практически автоматическими. Это один из главных, но часто игнорируемых аргументов в пользу инвестиций в архитектуру с первого дня.

Тестовая пирамида и архитектурные слои

Классическая тестовая пирамида отображается один-к-одному на архитектурные слои мобильного приложения:

  • Unit-тесты (основа пирамиды). Тестируют domain-слой: use cases, бизнес-правила, валидацию. Не требуют Android SDK или iOS-эмулятора — запускаются на JVM/машине разработчика за секунды. Если domain-слой написан правильно (без зависимостей от Android или UIKit), покрыть его unit-тестами не составляет труда.
  • Интеграционные тесты (средний уровень). Тестируют взаимодействие data-слоя с локальной БД (Room InMemory database) и сетевым слоем (MockWebServer). Проверяют, что Repository корректно кэширует, возвращает ошибки и синхронизирует данные.
  • UI/End-to-End тесты (вершина пирамиды). Espresso и Compose UI Test на Android; XCUITest на iOS. Тестируют сценарии целиком — от нажатия кнопки до отображения результата. Медленные, но незаменимы для критических путей (авторизация, оформление заказа).

Что CI проверяет автоматически

При правильной организации пайплайна CI закрывает большинство архитектурных регрессий автоматически. Типичный пайплайн для мобильного приложения:

Стадия CI Что проверяется Инструменты Время
Статический анализ Code style, архитектурные зависимости (нет импортов Android в domain), lint Detekt, SwiftLint, ArchUnit 1–2 мин
Unit-тесты Domain use cases, ViewModel logic, Repository stubs JUnit5, MockK (Android); XCTest (iOS) 2–5 мин
Интеграционные тесты Room InMemory DB, MockWebServer, DataSource MockWebServer, Robolectric 5–10 мин
Сборка Debug/release APK или IPA, размер бинарника Gradle, Xcode 5–15 мин
UI-тесты (по расписанию) Критические пользовательские сценарии Espresso, XCUITest, Firebase Test Lab 15–40 мин

ArchUnit: автоматический контроль архитектурных границ

Один из мощных, но редко используемых инструментов — ArchUnit (Java/Kotlin) и его аналоги. ArchUnit позволяет закодировать архитектурные правила как тесты:

  • «Классы в пакете domain не должны зависеть от android.*»
  • «ViewModel не должна напрямую создавать репозитории — только через DI»
  • «Repository-интерфейсы в domain; реализации — только в data»

Эти правила запускаются в CI на каждый PR и отклоняют изменения, нарушающие архитектурные инварианты. Это избавляет от необходимости объяснять архитектуру на каждом code review — машина делает это за вас.

CD: доставка сборок и управление конфигурациями

Непрерывная доставка для мобильных приложений имеет специфику: в отличие от веба, нельзя мгновенно откатиться после выпуска. Архитектура напрямую влияет на надёжность CD-процесса:

  • Feature flags (флаги функциональности). Новая фича закрыта флагом и деплоится в выключенном виде. Включается удалённо без релиза в стор. Реализуется в presentation-слое через ViewModel — business-логика всегда готова, UI включается по флагу.
  • Versioned API contracts. Data-слой с репозиторием-абстракцией позволяет переключаться между версиями API без изменения domain-слоя. Полезно при миграции бэкенда или A/B-тестировании.
  • Staged rollout + crashlytics. Постепенный выкат (1% → 10% → 50% → 100%) в сочетании с мониторингом крэшей (Firebase Crashlytics, Sentry) позволяет поймать архитектурные баги до того, как они дойдут до всех пользователей.

Часто задаваемые вопросы

Чем архитектура мобильного приложения отличается от технологического стека?

Технологический стек — это набор языков, фреймворков и инструментов. Архитектура — это структура и принципы организации кода: как разделены слои, как данные движутся между ними, как компоненты общаются. Можно использовать один стек (Kotlin + Jetpack), но реализовывать разные архитектуры — MVVM или MVI.

Какой архитектурный паттерн выбрать для стартапа или MVP?

Для MVP и небольших приложений оптимален MVVM: он широко поддерживается Google (Jetpack ViewModel) и Apple (SwiftUI + Observation), имеет низкий порог входа и достаточную тестируемость. Добавлять Clean Architecture или MVI на старте не стоит — лишние слои абстракции замедлят разработку без реальной пользы при маленькой кодовой базе.

Нужна ли Clean Architecture маленькому приложению?

Нет. Clean Architecture оправдана при сложной бизнес-логике, большой команде и длинном жизненном цикле продукта. Для приложения с 5–10 экранами и простыми сценариями она создаёт избыточную сложность и увеличивает время разработки без видимой пользы.

Отличается ли архитектура для нативной разработки и Flutter или React Native?

Принципы слоёв и паттернов одинаковы — presentation/domain/data, MVVM или MVI работают везде. Разница в инструментах: нативная Android использует Jetpack ViewModel и Coroutines, Flutter — BLoC или Riverpod, React Native — Redux или Zustand. Кросс-платформа даёт единый domain-слой, но UI-слой всё равно платформенный.

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

Продуманная архитектура снижает стоимость поддержки: изолированные модули проще менять без риска регрессий, тестируемый код снижает число багов в проде, а чёткое разделение слоёв позволяет подключить нового разработчика быстро. Монолитная архитектура экономит время в начале, но кратно увеличивает расходы на каждую следующую доработку.

Как проверить архитектуру мобильного приложения, не читая весь код?

Три точки проверки: 1) domain use case — он не должен импортировать Android SDK или UIKit; 2) ViewModel — не должна иметь прямой ссылки на Activity/Fragment/View; 3) Repository — бизнес-логика зависит от интерфейса, а не от конкретного Retrofit-сервиса. Если все три условия выполнены, ключевые принципы разделения слоёв соблюдены.

Можно ли сменить команду разработки, не переписывая приложение?

Да — если у приложения есть явные слои с задокументированными интерфейсами. Новая команда онбордится по контрактам Repository, UseCase и UiState, а не по спагетти-логике в Activity. Если же логика размазана по UI — смена команды де-факто требует переписывания, потому что нет точки опоры для передачи знаний.

Как связаны архитектура мобильного приложения и CI/CD?

Правильная архитектура делает CI/CD практически автоматическим: domain-слой без зависимостей от Android/iOS покрывается unit-тестами без эмулятора за секунды, ArchUnit закодирует архитектурные правила как тесты и проверяет их при каждом PR, а feature flags в presentation-слое позволяют деплоить новые функции без риска — в выключенном виде.

Заключение

Архитектура мобильного приложения — это не академическая теория, а инструмент управления стоимостью разработки и скоростью релизов. Правильно выбранные слои и паттерны превращают каждую новую фичу в предсказуемую задачу с понятной трудоёмкостью. Неправильная архитектура превращает любое изменение в рулетку.

Для большинства новых проектов хорошей отправной точкой будет MVVM с трёхслойной моделью presentation/domain/data — это золотая середина между простотой и масштабируемостью. По мере роста продукта и команды архитектуру можно эволюционно усложнять, не переписывая с нуля. Если вы строите мобильный продукт и хотите заложить правильный фундамент с первого дня — мы в YuSMP Group занимаемся разработкой мобильных приложений под ключ: от выбора архитектуры до выпуска в сторы.

Нужно мобильное приложение?

Спроектируем и разработаем приложение под iOS и Android под вашу задачу.

Обсудить приложение