Архитектура Суперапп на Android: Правила выживания
Суперапп — это не просто большое приложение. Это экосистема, где мессенджер, соцсеть, музыка и видео живут под одной крышей. И главная боль здесь не время компиляции. Боль — это хаос в коммуникации между сервисами, костыли и архитектурный долг, который копится с каждым спринтом.
Когда команд пять, а модулей — пятьдесят, без строгих правил проект превращается в карточный домик. Вот некоторые правила, которые я вывел на практике.
Правило 1: Data-слой живёт с фичей
Глобальный модуль данных — это ловушка. Репозиторий чатов и репозиторий аудиоплеера не имеют ничего общего. У них разная логика кэширования, частота инвалидации, модели ошибок. Каждый сервис получает свой data-слой внутри собственного модуля реализации. Это даёт полную инкапсуляцию: изменения схемы базы данных в мессенджере не затрагивают плеер.
Правило 2: UI — отдельный модуль
Оставлять UI-компоненты в фича-модулях для супераппа — путь к визуальному хаосу. Когда кнопка в мессенджере отличается от кнопки в плеере, страдает пользовательский опыт. Дизайн-система живёт в отдельном модуле, независимом от бизнес-логики. Это позволяет менять визуальный язык приложения централизованно.
Правило 3: Коммуникация только через API-контракты
Настоящая боль — когда разработчики связывают модули костыльно: прямыми импортами, магическими строками в Intent или шиной событий без контрактов. Жёсткое правило: модули не знают о существовании друг друга, только об интерфейсах. Каждый сервис публикует модуль с публичным API, а реализация скрыта. Другие сервисы зависят исключительно от контрактов.
Правило 4: Навигация — отдельный модуль
Прятать навигацию в модуль приложения — ошибка. Фича-модули до неё просто не достучатся. Навигация между сервисами выделяется в самостоятельный модуль. В нём живут интерфейсы навигаторов, общие маршруты и реализации роутеров. Модуль приложения только инициализирует навигацию, но не владеет её контрактами.
Правило 5: DI живёт внутри фичи
Не нужно выносить конфигурацию внедрения зависимостей в отдельные Gradle-модули. Достаточно организовать структуру пакетов внутри каждой фичи: data, domain, ui и di. Модуль приложения импортирует все фичи и собирает граф зависимостей на верхнем уровне. При этом сам DI-код остаётся рядом с кодом, который он конфигурирует.
Правило 6: Изоляция через навигационные интерфейсы
Прямой вызов Activity из одного модуля в другой нарушает изоляцию. Вместо этого используются навигационные интерфейсы, опубликованные в модуле навигации. Реализация живёт в модуле сервиса, а интерфейс доступен всем. Любой сервис может инициировать переход, не зная ничего о внутреннем устройстве целевого экрана.
Сухой остаток
Эти правила не про красивый код. Они про выживание продукта с пятью командами и сотнями тысяч строк. Когда каждый сервис изолирован, а коммуникация идёт строго по контрактам — архитектура выдерживает рост и не превращается в монолит, который страшно трогать.
А с какими архитектурными вызовами сталкивались вы при построении крупных приложений?