Architecture Basics
Современная Android-архитектура разделяет UI, управление состоянием, бизнес-правила и доступ к данным. Благодаря этому у каждой части есть понятная ответственность и возможность развиваться независимо.
Эта страница посвящена архитектуре внутри Android-приложения. Проектирование взаимодействия мобильного клиента, API, сервисов, баз данных, кэшей, очередей и внешних систем разобрано в Основах System Design.
Слои
Современная Android-архитектура
Типичное Android-приложение состоит из UI-слоя, слоя данных и, при необходимости, доменного слоя. Это практическое применение принципа разделения ответственности, а не требование создавать одинаковые папки и классы в каждой фиче.
UI наблюдает за неизменяемым состоянием и передаёт действия пользователя обратно владельцу состояния, обычно ViewModel. Владелец состояния координирует работу через репозитории или use case-ы и публикует результат как UI state. Так формируется однонаправленный поток данных:
Activity, Fragment и composable-функции должны отвечать прежде всего за отображение и взаимодействие с UI и платформой. В них не стоит помещать детали доступа к данным и бизнес-правила, которым нужны более долгоживущие и независимо тестируемые компоненты.
UI-слой / слой данных / доменный слой
UI-слой отображает данные приложения и обрабатывает действия пользователя. К нему относятся composable-функции или Views, Android UI components, владельцы состояния экрана, UI-модели и форматирование для отображения.
Слой данных предоставляет данные приложения через репозитории. Он координирует удалённые и локальные источники, например API services, Room, DataStore, файлы и кэш, а также владеет правилами чтения, изменения, преобразования и синхронизации данных. UI-код не должен напрямую зависеть от Retrofit services, DAO или конкретных источников данных.
Доменный слой необязателен. В нём размещают use case-ы или interactors, когда бизнес-логика сложна, переиспользуется несколькими владельцами состояния или объединяет несколько репозиториев. На простом экране ViewModel может обращаться к репозиторию напрямую, и это не нарушает архитектуру.
Границы слоёв должны ясно определять зависимости: UI зависит от API доменного слоя или слоя данных, а слой данных не зависит от Android UI.
Clean Architecture
Clean Architecture отделяет бизнес-правила от UI-фреймворков и деталей реализации. Согласно dependency rule, зависимости в исходном коде направлены к стабильным правилам или абстракциям. Схема UI -> ViewModel -> use case -> repository -> data source обычно показывает вызовы и движение данных, но сама по себе не задаёт направление всех compile-time dependencies.
В Android этот подход стоит применять прагматично. Интерфейсы полезны, когда реализации могут меняться, тестам нужна подмена зависимости или требуется изоляция модулей. Необязательно создавать use case, mapper и interface для каждой тривиальной операции. Ясное владение логикой и тестируемость должны оправдывать дополнительную косвенность.
Когда нужен доменный слой?
Добавляйте доменный слой, когда он упрощает фичу, например если:
- бизнес-правила достаточно сложны и должны тестироваться отдельно;
- одна операция переиспользуется несколькими
ViewModel; - один сценарий объединяет несколько репозиториев;
- validation, authorization или error mapping сложнее UI-форматирования;
- workflow координирует несколько шагов или источников данных.
Типичные примеры - платежи, checkout и проверка доступности подписки. Для загрузки списка из одного репозитория отдельный use case обычно не нужен.
Владение данными
Repository pattern
Репозиторий - публичная точка входа в определённую часть слоя данных. Он скрывает, поступают ли данные из сети, базы, кэша, DataStore, файла или websocket, и предоставляет остальному приложению осмысленные операции.
Репозиторий может координировать источники, преобразовывать модели, задавать правила кэширования и реализовывать offline-first поведение. Он не должен превращаться в несвязанное хранилище всей бизнес-логики. Даже небольшая реализация полезна, если формирует ясную границу слоя данных, но дополнительные обёртки внутри неё должны оправдывать свою сложность.
Single source of truth
Single source of truth означает, что у каждого типа данных приложения есть один авторитетный владелец. Остальные компоненты наблюдают за его данными или запрашивают изменения через него, а не хранят конкурирующие изменяемые копии.
В offline-first фиче источником истины часто выступает локальная база: ответы сети обновляют её, а UI наблюдает за ней через репозиторий. В более простой фиче источником может быть backend или состояние в памяти под управлением репозитория. Выбор зависит от требований к сохранению, offline-работе и согласованности данных.
К UI state применяется тот же принцип владения. Экран должен рендериться из состояния одного владельца, а не собирать его из несвязанных mutable-полей в UI. Действия пользователя возвращаются владельцу, который обновляет state. Это уменьшает риск рассинхронизации, race conditions и ошибок после пересоздания экрана.