Архитектура мобильных приложений: полное руководство по проектированию
Как правильно спроектировать архитектуру приложения, чтобы оно было масштабируемым, поддерживаемым и надёжным [2026–2027]
Представьте, что вы построили дом без фундамента — просто поставили стены на землю. Первое время всё будет выглядеть нормально, но стоит пойти дождям или ударить морозам, и конструкция начнёт рушиться. То же самое происходит с мобильными приложениями без продуманной архитектуры.
Архитектура мобильного приложения — это не абстрактное понятие для разработчиков. Это основа, которая определяет, насколько быстро вы сможете добавлять новые функции, как легко будет исправлять баги, и сможет ли приложение расти вместе с вашим бизнесом. Неправильная архитектура на старте — это технический долг, который рано или поздно придётся платить переписыванием с нуля.
В этой статье мы разберём основные архитектурные паттерны, поймём, когда какой использовать, и научимся избегать типичных ошибок при проектировании.
Содержание
- Что такое архитектура мобильного приложения
- Почему архитектура критична для бизнеса
- Ключевые архитектурные паттерны
- MVVM: самый популярный выбор
- MVI: предсказуемость на первом месте
- Clean Architecture: разделяй и властвуй
- Структура мобильного приложения по слоям
- Проектирование приложения: пошаговый процесс
- Типичные архитектурные ошибки
- Как выбрать архитектуру для вашего проекта
Ключевые моменты
1. Что такое архитектура мобильного приложения
Прежде чем погружаться в паттерны и технические детали, важно понять саму суть архитектуры. Без этого понимания легко запутаться в терминах и сделать выбор, основанный на моде, а не на реальных потребностях проекта.
Архитектура мобильного приложения — это структура системы, которая определяет, как компоненты приложения взаимодействуют друг с другом. Проще говоря, это «чертёж» вашего приложения: какие части существуют, за что каждая отвечает и как они общаются между собой.
Из чего состоит архитектура
Любое мобильное приложение, независимо от его сложности, можно разделить на несколько логических уровней. Каждый уровень решает свою задачу, и понимание этого разделения — первый шаг к грамотному проектированию.
Presentation Layer (слой представления) — то, что видит пользователь. Экраны, кнопки, анимации, формы ввода. Этот слой отвечает за отображение данных и обработку действий пользователя. Здесь живут все визуальные элементы: от экрана авторизации до сложных дашбордов с графиками.
Business Logic Layer (слой бизнес-логики) — «мозг» приложения. Здесь происходят расчёты, валидация данных, принятие решений. Например, логика формирования корзины, расчёт стоимости доставки или проверка прав доступа. Это ядро, которое отличает ваше приложение от конкурентов и содержит всю уникальную ценность продукта.
Data Layer (слой данных) — работа с источниками информации. Запросы к серверу, сохранение в локальную базу, кэширование. Этот слой знает, откуда взять данные и куда их сохранить, но не знает, как эти данные будут использоваться.
Архитектура vs код
Важно понимать разницу между этими понятиями, потому что от неё зависит стоимость изменений в будущем. Код — это конкретная реализация, строки программы. Архитектура — это принципы организации этого кода.
Плохой код можно переписать за несколько дней — достаточно опытного разработчика и времени. Плохую архитектуру приходится переделывать месяцами, потому что она затрагивает всё приложение целиком: изменение в одном модуле требует правок в десятках других.
Нужна экспертная оценка архитектуры?
Проведём аудит вашего мобильного приложения и дадим рекомендации по улучшению структуры кода.
2. Почему архитектура критична для бизнеса
По данным исследований в области DevOps и автоматизированного тестирования, команды с качественной архитектурой выпускают обновления в 46 раз чаще и имеют в 7 раз меньше критических багов. Это не абстрактные цифры — это разница между еженедельными релизами и месяцами ожидания.
Стоимость технического долга
Технический долг — это компромиссы, на которые идёт команда ради скорости. «Сделаем быстро, потом перепишем» — классическая фраза на kickoff-встречах. Проблема в том, что «потом» никогда не наступает, и долг накапливается с каждым спринтом.
На первом году разработки технический долг даже может казаться преимуществом: команда работает быстрее конкурентов. Но уже на втором году ситуация меняется — накопленные проблемы начинают тормозить разработку. К третьему году картина становится критичной.
На третий год команда с техническим долгом тратит больше времени на исправление старых проблем, чем на создание нового функционала. А бизнес удивляется: «Почему раньше делали быстрее?» Ответ прост — долг пришёл за оплатой.
Масштабирование команды
Когда приложение успешно, команду нужно расширять. И здесь архитектура снова выходит на первый план. С хорошей архитектурой новые разработчики начинают приносить пользу через 2-4 недели — структура понятна, код читаем, зависимости очевидны.
С плохой — через 2-4 месяца, потому что понять хаос невозможно без «священного знания» старожилов. Каждый новый человек в команде требует часов объяснений от опытных коллег, которые вместо разработки занимаются онбордингом.
Реальный кейс: цена неправильного выбора
В нашей практике был проект — приложение для логистической компании. Первая версия была написана без чёткой архитектуры — «быстро и дёшево». На старте это казалось разумным: нужно было проверить гипотезу и выйти на рынок.
Через полтора года, когда бизнес вырос и потребовались новые функции, команда потратила 4 месяца только на рефакторинг, прежде чем смогла добавить новый модуль. Код был настолько переплетён, что изменение в одном месте приводило к непредсказуемым багам в других частях приложения.
Если бы архитектура была заложена правильно с самого начала, те же изменения заняли бы 3-4 недели. А главное — упущенное рыночное окно, когда конкуренты уже запустили аналогичный функционал.
3. Ключевые архитектурные паттерны
Теперь, когда мы понимаем бизнес-значимость архитектуры, пора перейти к конкретным инструментам. Хорошая новость: не нужно изобретать велосипед. За десятилетия индустрия выработала проверенные подходы к организации кода.
Архитектурный паттерн — это проверенное решение типичной проблемы проектирования. Вместо того чтобы каждый раз придумывать структуру с нуля, команды используют готовые подходы, которые доказали свою эффективность на тысячах проектов. Это экономит время на старте и упрощает коммуникацию — все понимают, что имеется в виду под «MVVM» или «Clean Architecture».
Обзор основных паттернов
Прежде чем детально разбирать каждый подход, полезно увидеть общую картину. В таблице ниже — краткая характеристика основных паттернов и типичные сценарии их применения.
MVC: исторический контекст
Чтобы понять, почему появились современные паттерны, полезно знать, откуда всё началось. MVC (Model-View-Controller) — один из первых архитектурных паттернов, появился ещё в 1970-х годах. В мобильной разработке он долгое время был стандартом — Apple предлагала его для iOS, Google — для Android.
Как работает: Controller получает действия пользователя, обновляет Model, Model уведомляет View об изменениях. Простая и понятная схема, которая отлично работает для небольших приложений.
Проблема: На практике Controller превращается в «Massive View Controller» — огромный класс, который делает всё: обрабатывает UI, выполняет бизнес-логику, работает с данными. Тестировать такой код практически невозможно — слишком много зависимостей и побочных эффектов.
Сегодня чистый MVC в мобильной разработке используется редко. Его сменили более современные подходы, которые лучше разделяют ответственности.
MVP: шаг вперёд
MVP (Model-View-Presenter) стал ответом на главную проблему MVC — переусложнённые контроллеры. Ключевая идея: вынести логику из View в отдельный компонент Presenter, который можно тестировать изолированно.
Как работает: View отображает данные и передаёт события Presenter. Presenter обрабатывает логику и говорит View, что показать. Model предоставляет данные. Каждый компонент имеет чётко определённую роль.
Преимущества:
- Presenter не зависит от UI-фреймворка, его можно тестировать unit-тестами
- Чёткое разделение ответственности между компонентами
- View становится «тонким» — только отображение, никакой логики
Недостатки:
- Много boilerplate-кода — интерфейсы, методы связи между компонентами
- Сложность синхронизации состояния между Presenter и View
- Нужно вручную управлять жизненным циклом и обновлениями
MVP был популярен в Android-разработке до появления Architecture Components в 2017 году. Сейчас его постепенно вытесняет MVVM, который решает те же задачи более элегантно.
4. MVVM: самый популярный выбор
Если вы спросите разработчика в 2026–2027 году, какую архитектуру использовать для нового проекта, в большинстве случаев ответ будет: MVVM. Это не случайность — паттерн стал стандартом благодаря поддержке платформ и балансу между сложностью и выразительностью.
MVVM (Model-View-ViewModel) — доминирующий архитектурный паттерн в мобильной разработке сегодня. Google официально рекомендует его для Android, Apple продвигает аналогичный подход в SwiftUI. Экосистемы обоих платформ построены вокруг этого паттерна.
Как работает MVVM
В основе MVVM лежит идея реактивного связывания: View автоматически обновляется при изменении данных в ViewModel, без явных вызовов «перерисуй экран».
View — экраны приложения. Отображает данные и отправляет действия пользователя. В идеале View не содержит логики — только декларативное описание интерфейса.
ViewModel — хранит состояние UI и бизнес-логику экрана. Не знает о конкретной реализации View, что позволяет тестировать ViewModel изолированно.
Model — данные и логика их получения. Репозитории, сетевые запросы, база данных. Этот слой ничего не знает о том, как данные будут отображаться.
Ключевая идея MVVM — реактивность. View подписывается на изменения ViewModel и автоматически обновляется при изменении данных. Не нужно вручную вызывать «обнови экран» — привязка работает автоматически.
User Action → View → ViewModel → Model
↑ ↓
← State Update ←
Преимущества MVVM
Тестируемость. ViewModel — обычный класс без UI-зависимостей. Его можно покрыть unit-тестами на 90%+, проверяя логику без запуска эмулятора или реального устройства.
Реактивность. Изменение данных автоматически отражается на экране. Меньше ручного кода означает меньше багов синхронизации, когда экран показывает устаревшие данные.
Поддержка платформой. Android Jetpack (ViewModel, LiveData, StateFlow), SwiftUI, Flutter — все основные платформы предоставляют нативную поддержку этого паттерна.
ViewModel хранит UI-состояние в StateFlow. Composable-функции (или XML-layout с DataBinding) подписываются на это состояние и автоматически перерисовываются при изменениях.
MVVM на практике: iOS
В iOS c SwiftUI архитектура практически идентична, что упрощает жизнь командам, работающим на обеих платформах:
- View — SwiftUI View, декларативное описание интерфейса
- ViewModel — ObservableObject класс, хранящий состояние
- Model — сервисы, репозитории, работа с данными
Связывание происходит через @Published свойства и @StateObject / @ObservedObject. SwiftUI автоматически отслеживает изменения и перерисовывает только те части UI, которые зависят от изменившихся данных.
Хотите внедрить MVVM в проект?
Покажем кейсы из вашей ниши: как правильная архитектура сокращает time-to-market.
5. MVI: предсказуемость на первом месте
MVVM отлично работает для большинства приложений, но у него есть ограничения. Когда состояние экрана становится сложным — множество взаимосвязанных флагов, асинхронные операции, влияющие друг на друга — управлять этим хаосом становится всё труднее. Именно для таких случаев был создан MVI.
MVI (Model-View-Intent) — эволюция MVVM для приложений со сложным состоянием. Если в вашем приложении много экранов, которые влияют друг на друга, много асинхронных операций и сложная логика переходов — MVI может быть лучшим выбором.
Принципы MVI
Главное отличие MVI от MVVM — строгость и предсказуемость. Вместо нескольких изменяемых переменных состояния, MVI использует единый неизменяемый объект, который полностью описывает экран.
Однонаправленный поток данных. Данные движутся только в одном направлении: от Intent к State. Нет обратных связей, которые могут создать неожиданные циклы.
Intent → Reducer → State → View
↑ ↓
← User Action ←
Неизменяемое состояние (Immutable State). Состояние нельзя изменить напрямую — можно только создать новое состояние на основе предыдущего и действия. Это исключает целый класс багов, связанных с гонками состояний.
Единый источник правды. Всё состояние экрана хранится в одном месте. Не нужно синхронизировать несколько переменных и гадать, какая из них актуальна.
Компоненты MVI
Каждый компонент MVI имеет строго определённую роль, и понимание этих ролей — ключ к правильному использованию паттерна.
Intent (намерение) — действие пользователя или системы. «Пользователь нажал кнопку», «Данные загружены», «Произошла ошибка». Intent не содержит логики — только описание того, что произошло.
Reducer (редьюсер) — чистая функция, которая принимает текущее состояние и Intent, возвращает новое состояние. Никаких побочных эффектов, никаких сетевых запросов — только трансформация данных.
State (состояние) — неизменяемый объект, полностью описывающий экран. Всё, что нужно для отображения, находится в State: данные, флаги загрузки, тексты ошибок.
View — отображает State. Когда State меняется — View перерисовывается. Никакой логики в View, только маппинг State на UI-элементы.
Преимущества MVI
Предсказуемость. Зная текущее состояние и входящий Intent, всегда можно предсказать следующее состояние. Это упрощает отладку и понимание кода.
Воспроизводимость багов. Можно записать последовательность Intent и воспроизвести баг на любом устройстве. Больше никаких «у меня работает, а у пользователя нет».
Time-travel debugging. Можно «перемотать» историю состояний и увидеть, что произошло в каждый момент. Это бесценно при расследовании сложных багов.
Сложные сценарии. MVI отлично справляется с экранами, где много асинхронных операций и сложная логика переходов — например, многошаговые формы или real-time обновления.
Когда выбирать MVI
Выбор между MVVM и MVI — не вопрос «что лучше», а вопрос соответствия задаче. Для простых экранов MVI избыточен, для сложных — необходим.
MVI: overhead или инвестиция?
MVI требует больше кода, чем MVVM. Каждый экран — это State-класс, набор Intent'ов, Reducer. Для простого приложения это избыточно и замедляет разработку без видимой пользы.
Но для сложного приложения с десятками экранов, командой из 5+ разработчиков и планами на многолетнее развитие — MVI окупается. Предсказуемость и тестируемость экономят время на отладке и поддержке, а строгая структура упрощает онбординг новых членов команды.
6. Clean Architecture: разделяй и властвуй
MVVM и MVI — это паттерны организации presentation-слоя. Они отвечают на вопрос «как связать UI с логикой экрана». Но когда приложение растёт, появляется другой вопрос: «как организовать всё приложение целиком?»
Clean Architecture — это не конкретный паттерн, а набор принципов организации кода, предложенный Робертом Мартином (Uncle Bob). Главная идея: разделить приложение на слои с чёткими зависимостями, где внутренние слои не знают о внешних.
Принцип зависимостей
В Clean Architecture зависимости направлены внутрь. Внешние слои знают о внутренних, но внутренние не знают о внешних. Это ключевой принцип, который обеспечивает гибкость и тестируемость.
[Frameworks & Drivers] → [Interface Adapters] → [Use Cases] → [Entities]
(внешний слой) (ядро)
Entities (сущности) — бизнес-объекты и правила, которые не зависят от приложения. Например, правила расчёта скидки или валидация данных. Это самый стабильный слой, который меняется редко.
Use Cases (сценарии использования) — бизнес-логика приложения. «Оформить заказ», «Получить список товаров», «Авторизовать пользователя». Каждый Use Case делает одно дело и делает его хорошо.
Interface Adapters (адаптеры) — преобразование данных между слоями. ViewModel, Presenter, Repository. Эти компоненты переводят данные из формата, удобного для Use Cases, в формат, нужный UI или базе данных.
Frameworks & Drivers — внешние зависимости. UI-фреймворк, база данных, сеть. Это самый изменчивый слой, который легко заменить без переписывания бизнес-логики.
Преимущества Clean Architecture
Независимость от фреймворков. Бизнес-логика не знает о Retrofit, Room или Compose. Можно заменить любой фреймворк, не трогая ядро — полезно при миграции на новые версии или смене технологий.
Тестируемость. Каждый слой тестируется изолированно. Use Cases — unit-тестами без моков инфраструктуры, Interface Adapters — с моками.
Понятная структура. Новый разработчик понимает, где искать бизнес-логику, где — работу с данными, где — UI. Код организован не по техническим критериям, а по функциональным.
Масштабируемость. Добавление нового Use Case не затрагивает существующие. Модули независимы, команда может работать параллельно над разными частями системы.
Clean Architecture на практике
Теория хороша, но как это выглядит в реальном проекте? Вот типичная структура папок:
app/
├── data/ # Data Layer
│ ├── api/ # Сетевые запросы
│ ├── database/ # Локальная БД
│ └── repository/ # Реализации репозиториев
├── domain/ # Domain Layer (чистый Kotlin/Swift)
│ ├── model/ # Бизнес-сущности
│ ├── repository/ # Интерфейсы репозиториев
│ └── usecase/ # Use Cases
└── presentation/ # Presentation Layer
├── feature1/ # Экраны фичи 1
└── feature2/ # Экраны фичи 2
Обратите внимание: domain-слой не содержит зависимостей от Android или iOS SDK. Это чистый Kotlin или Swift, который можно тестировать без эмулятора.
Когда Clean Architecture избыточна
Clean Architecture добавляет слои абстракции и код. Для простых приложений это может быть overkill — вы потратите время на создание структуры, которая не окупится.
Главное правило: сложность архитектуры должна соответствовать сложности продукта. Не стоит строить небоскрёб, когда нужен дачный домик.
Планируете сложный мобильный проект?
На бесплатной консультации определим оптимальный архитектурный подход и дадим оценку сроков.
7. Структура мобильного приложения по слоям
Теперь, когда мы разобрали теорию, давайте посмотрим, как на практике выглядит слоёная архитектура в современном мобильном приложении. Это поможет связать абстрактные концепции с конкретным кодом.
Presentation Layer
Слой представления отвечает за UI и взаимодействие с пользователем. Это «лицо» вашего приложения — всё, что видит и с чем взаимодействует пользователь.
Компоненты:
- Screens / Views — экраны приложения, декларативное описание UI
- ViewModels — хранят состояние и логику UI, связывают экран с бизнес-логикой
- UI State — неизменяемые классы состояния, полностью описывающие экран
- Navigation — логика переходов между экранами
Принципы:
- ViewModel не знает о конкретной реализации View — это обеспечивает тестируемость
- UI State содержит всё необходимое для отрисовки экрана — никаких скрытых зависимостей
- Нет бизнес-логики — только логика отображения и взаимодействия
Domain Layer
Слой бизнес-логики — сердце приложения. Здесь живёт вся уникальная ценность вашего продукта, то, что отличает его от конкурентов.
Компоненты:
- Use Cases — бизнес-операции. Каждый Use Case делает одно дело
- Entities — бизнес-объекты и правила их валидации
- Repository Interfaces — контракты для работы с данными
Принципы:
- Чистый Kotlin/Swift — никаких зависимостей от фреймворков. Это самый портабельный слой
- Один Use Case — одно действие. «Оформить заказ», не «Управлять заказами»
- Валидация и бизнес-правила живут здесь, не в UI и не в data-слое
Пример Use Case:
class PlaceOrderUseCase(
private val orderRepository: OrderRepository,
private val paymentService: PaymentService
) {
suspend fun execute(order: Order): Result<OrderConfirmation> {
// Валидация
if (order.items.isEmpty()) return Result.Error("Empty cart")
// Бизнес-логика
val payment = paymentService.process(order.total)
if (payment.isFailure) return Result.Error("Payment failed")
// Сохранение
return orderRepository.create(order)
}
}
Data Layer
Слой данных отвечает за получение и сохранение информации. Он скрывает от бизнес-логики детали работы с API, базой данных и кэшем.
Компоненты:
- Repository Implementations — реализации интерфейсов из Domain
- Data Sources — источники данных (API, база, кэш)
- DTOs — объекты для передачи данных, специфичные для API или базы
- Mappers — преобразование DTO ↔ Entity
Принципы:
- Repository решает, откуда взять данные (сеть, кэш, база) — это его ответственность
- Data Source знает специфику конкретного источника — формат запросов, обработку ошибок
- Domain Layer не знает о деталях реализации — только о контрактах
Взаимодействие слоёв
На практике данные текут через слои сверху вниз (от пользователя к хранилищу) и обратно:
┌─────────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ ┌─────────┐ ┌───────────────┐ ┌─────────────────┐ │
│ │ View │ ←→│ ViewModel │ ←→│ Navigation │ │
│ └─────────┘ └───────────────┘ └─────────────────┘ │
│ │ │
│ ↓ │
├─────────────────────────────────────────────────────────────┤
│ Domain Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌────────────────┐ │
│ │ Use Cases │←→│ Entities │ │ Repositories │ │
│ │ │ │ │ │ (interfaces) │ │
│ └─────────────┘ └─────────────┘ └────────────────┘ │
│ ↑ │
│ │ │
├─────────────────────────────────────────────────────────────┤
│ Data Layer │
│ ┌────────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ Repositories │←→│ Data Sources │←→│ Mappers │ │
│ │(implementations)│ │ (API, DB) │ │ │ │
│ └────────────────┘ └──────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────┘
8. Проектирование приложения: пошаговый процесс
Теория изучена, паттерны понятны — но как применить эти знания на практике? Вот пошаговый процесс проектирования архитектуры, который мы используем в Surf для каждого нового проекта.
Шаг 1: Анализ требований
Прежде чем выбирать паттерны и технологии, нужно понять контекст. Архитектура — не самоцель, а инструмент для решения конкретных задач. Разные задачи требуют разных инструментов.
Вопросы о бизнесе:
- Какой срок жизни продукта? (MVP на полгода или платформа на 5 лет)
- Какой размер команды сейчас и в перспективе?
- Насколько критичны стабильность и скорость развития?
- Какие ресурсы доступны на поддержку?
Вопросы о продукте:
- Сколько экранов и функций в первой версии и в roadmap?
- Какая сложность бизнес-логики?
- Нужен ли оффлайн-режим?
- Какие интеграции с внешними системами?
Шаг 2: Выбор технологического стека
Архитектура зависит от платформы и выбранных технологий. Каждая платформа имеет свои идиомы и лучшие практики.
Шаг 3: Выбор архитектурного паттерна
На основе анализа требований выбираем подход. Не существует универсально правильного ответа — только подходящий для конкретной ситуации.
Шаг 4: Определение модулей
Разбиение на модули — критически важное решение, которое влияет на скорость сборки, возможность параллельной работы и переиспользование кода.
По фичам (Feature-based):
features/
├── auth/
├── catalog/
├── cart/
├── checkout/
├── profile/
└── orders/
По слоям (Layer-based):
app/
├── data/
├── domain/
└── presentation/
Гибридный подход (рекомендуем):
core/ # Общий код
├── data/
├── domain/
└── ui/
features/ # Фичи
├── auth/
│ ├── data/
│ ├── domain/
│ └── presentation/
└── catalog/
├── data/
├── domain/
└── presentation/
Гибридный подход сочетает преимущества обоих методов: фичи изолированы для параллельной разработки, а общий код вынесен в core-модули.
Шаг 5: Определение контрактов
Зафиксируйте интерфейсы между слоями до начала разработки. Это позволяет командам работать параллельно: один разрабатывает UI, другой — бизнес-логику, третий — интеграции.
- Какие данные передаёт Repository?
- Какие методы выставляет UseCase?
- Какие события отправляет ViewModel?
Чек-лист проектирования
Перед началом разработки убедитесь, что все ключевые решения приняты и зафиксированы:
- [ ] Проанализированы бизнес-требования
- [ ] Выбран технологический стек
- [ ] Определён архитектурный паттерн
- [ ] Разбиты модули приложения
- [ ] Зафиксированы контракты между слоями
- [ ] Определены инструменты для DI, навигации, работы с сетью
- [ ] Подготовлен шаблон проекта с базовой структурой
Ищете команду для проектирования?
250+ специалистов, 300+ проектов. Проектируем архитектуру, которая масштабируется вместе с бизнесом.
9. Типичные архитектурные ошибки
Мы видели десятки проектов с архитектурными проблемами. Некоторые паттерны повторяются снова и снова — независимо от размера компании, опыта команды или бюджета. Вот самые распространённые ошибки и способы их избежать.
Ошибка 1: «God Object» — объект-бог
Проблема: Один класс делает слишком много. ViewModel на 2000 строк, который обрабатывает 15 разных экранов. Activity, которая одновременно работает с сетью, базой данных и UI.
Почему это плохо: Невозможно тестировать — слишком много зависимостей. Сложно понять — нужно держать в голове весь контекст. Изменение в одном месте ломает другое — неочевидные связи.
Решение: Single Responsibility Principle. Один класс — одна ответственность. Разбивайте крупные классы на мелкие, специализированные. ViewModel для одного экрана, не для группы экранов.
Ошибка 2: Бизнес-логика в UI
Проблема: Расчёт скидки происходит прямо в Composable-функции. Валидация формы — в обработчике клика. Решение о навигации — в самом экране.
Почему это плохо: Логику невозможно переиспользовать. При изменении UI нужно переносить логику. Тестирование только через UI-тесты — медленно и нестабильно.
Решение: Выносите бизнес-логику в UseCase или ViewModel. UI должен только отображать готовые данные и передавать события.
Ошибка 3: Игнорирование асинхронности
Проблема: Синхронные вызовы в UI-потоке блокируют интерфейс. Отсутствие обработки состояний загрузки и ошибок — пользователь видит пустой экран.
Почему это плохо: Приложение «зависает» во время сетевых запросов. Пользователь не понимает, что происходит. Crash при ошибках сети превращает баг в катастрофу.
Решение: Всегда моделируйте состояния: Loading, Success, Error. Используйте корутины (Kotlin) или async/await (Swift). Показывайте пользователю, что происходит.
Ошибка 4: Жёсткая связанность
Проблема: Классы напрямую создают свои зависимости. UserViewModel создаёт UserRepository, который создаёт ApiClient. Цепочка зависимостей тянется через всё приложение.
Почему это плохо: Невозможно подменить зависимость для тестирования. Изменение в одном классе требует изменений во всех зависимых. Нельзя переиспользовать компоненты в другом контексте.
Решение: Dependency Injection. Hilt для Android, built-in DI для SwiftUI, GetIt для Flutter. Зависимости передаются извне, а не создаются внутри.
Ошибка 5: Преждевременная оптимизация
Проблема: Микросервисная архитектура для MVP. Сложные паттерны в приложении из 5 экранов. Clean Architecture с Use Cases для CRUD-операций.
Почему это плохо: Overhead на поддержку сложности съедает время. Медленная разработка — конкуренты обгоняют. Команда тратит время на архитектуру вместо продукта.
Решение: Выбирайте сложность, соответствующую проекту. Для MVP — простой MVVM. Усложняйте по мере роста, когда появляется реальная потребность.
Ошибка 6: Отсутствие архитектуры
Проблема: «Сейчас быстро сделаем, потом перепишем». Код пишется без структуры, каждый разработчик делает по-своему. Нет единых соглашений.
Почему это плохо: Через полгода — хаос. Добавление фичи занимает в 5 раз больше времени. Новые разработчики месяцами входят в проект, потому что нет логики в организации кода.
Решение: Выделите время на проектирование в начале. 1-2 недели на архитектуру сэкономят месяцы переделок.
Видите проблемы в архитектуре текущего проекта?
Проведём технический аудит кода и дадим конкретные рекомендации по рефакторингу с приоритетами.
10. Как выбрать архитектуру для вашего проекта
Универсальной «лучшей» архитектуры не существует. Выбор зависит от контекста: размера проекта, команды, бюджета, сроков. Попытка применить enterprise-архитектуру к MVP — такая же ошибка, как отсутствие архитектуры в крупном проекте.
Матрица выбора
Вот практические рекомендации для типичных сценариев:
Пошаговый алгоритм выбора
Если таблица не даёт однозначного ответа, пройдите по этим шагам:
Шаг 1: Оцените сложность продукта
- Простой (< 15 экранов, CRUD-операции) → MVVM
- Средний (15-40 экранов, интеграции) → MVVM + Clean Architecture
- Сложный (40+ экранов, сложная бизнес-логика) → MVI + Clean Architecture
Шаг 2: Оцените размер команды
- 1-2 разработчика → Проще лучше, меньше абстракций
- 3-5 разработчиков → Нужна структура для параллельной работы
- 5+ разработчиков → Строгие контракты, модульность обязательна
Шаг 3: Оцените срок жизни продукта
- MVP для проверки гипотезы → Минимальная архитектура
- Продукт на 2-3 года → Средняя сложность
- Платформа на 5+ лет → Максимальная гибкость и масштабируемость
Шаг 4: Оцените критичность
- Некритичный сервис → Можно позволить компромиссы
- Критичный (платежи, здоровье, безопасность) → Максимальная надёжность
Наш подход в Surf
Мы используем адаптивный подход к архитектуре, подбирая сложность под задачу:
Для MVP и небольших проектов: MVVM с чётким разделением на слои, но без избыточных абстракций. Это позволяет быстро выйти на рынок и проверить гипотезу.
Для средних проектов: MVVM + Clean Architecture. Разделение на data/domain/presentation даёт гибкость для роста, при этом не перегружая кодовую базу.
Для крупных проектов: MVI + Clean Architecture + модульность. Каждая фича — отдельный модуль со своей архитектурой. Это обеспечивает независимость команд и предсказуемость поведения.
Главный принцип: архитектура должна решать проблемы, а не создавать их. Если паттерн мешает, а не помогает — значит, он выбран неправильно.
Заключение
Архитектура мобильного приложения — это не академическое упражнение и не дань моде. Это практический инструмент, влияющий на успех продукта, скорость развития и стоимость поддержки. Правильный выбор на старте экономит месяцы работы.
Резюме ключевых решений
5 принципов хорошей архитектуры
- Разделение ответственности. Каждый компонент делает одно дело и делает его хорошо. Нет «god objects», нет смешения логики с отображением.
- Зависимости направлены внутрь. UI зависит от логики, логика не знает об UI. Это обеспечивает гибкость и тестируемость.
- Тестируемость по умолчанию. Код, который нельзя протестировать — это долг, который придётся оплатить багами в продакшене.
- Простота над сложностью. Не усложняйте, пока не появится необходимость. MVP не требует enterprise-архитектуры.
- Эволюция, не революция. Архитектура должна позволять постепенные изменения без переписывания с нуля.
Готовы начать проект с правильной архитектурой?
Расскажите о задаче — предложим подход, стек и план действий. Начнём с discovery-сессии.