Які проблеми вирішує модульна архітектура?
Monolith-застосунок з 50+ екранами, де все в одному модулі — це історія про те, як команда з 8 розробників чекає 4 хвилини повної збірки на кожну зміну, і як фіча однієї команди ламає тести іншої, тому що обидва імпортують один і той же singleton. Модульна архітектура вирішує конкретно ці проблеми. Ми допомагаємо командам модуляризувати застосунки від 20 до 200 екранів, спираючись на Apple Human Interface Guidelines та багаторічний досвід. Послуга модуляризації включає проектування модульної схеми, реалізацію core-модулів, міграцію фіч та налаштування збірки.
-
Швидкість збірки. При зміні одного файлу в feature-модулі перезбирається тільки він. Модуляризація скорочує інкрементальну збірку в 3–5 разів — з 7–11 хвилин до 45–90 секунд (реальний кейс).
- Merge-конфлікти. Коли декілька команд працюють в одному
.pbxproj або build.gradle, конфлікти неминучі. Модулі ізолюють код: feature-гілки рідко перетинаються.
- Ізоляція тестів. Тести одного модуля не залежать від змін в іншому. Можна запускати тільки тести порушеного модуля.
Як ми це робимо: стек і практики
Розділення на модулі
Стандартний підхід — розділення за фічами з загальним шаром:
:app — точка входу, DI-граф, навігація
:core:network — HTTP клієнт, interceptors
:core:storage — БД, SharedPreferences/DataStore
:core:ui — загальні компоненти, тема
:core:auth — токени, AuthInterceptor
:feature:profile — профіль користувача
:feature:catalog — каталог товарів
:feature:checkout — оформлення замовлення
:feature:orders — історія замовлень
Кожен :feature:* модуль залежить тільки від :core:* і не знає про інші фічі. Навігація між фічами — через абстракцію: NavigationManager в :core, яку кожна фіча використовує для переходу без прямого імпорту іншого feature-модуля. На Android — Navigation Component з NavGraph на рівні :app, deep links реєструються в маніфестах модулів. На iOS аналогічно — Coordinator pattern або Router з protocol-based навігацією.
Android. Кожен :feature:* — окремий Gradle-модуль (:feature:catalog → catalog/build.gradle.kts). Gradle Configuration Cache + Build Cache (~/.gradle/caches) радикально скорочують час перезбірки: змінюємо тільки :feature:checkout — перезбирається тільки він. --configuration-cache в Gradle 8 стабільний. Kapt → KSP: міграція annotation processor'ів з Kapt на KSP (Kotlin Symbol Processing) дає +30–50% до швидкості генерації коду.
iOS. Модулі реалізуються через Swift Package Manager (локальні) або як Framework targets в Xcode workspace. Swift Package Manager підтримує локальні пакети через path: в Package.swift. Workspace з декількома проектами (Feature1.xcodeproj, Feature2.xcodeproj) — застарілий підхід, новий — монорепо з локальними SPM пакетами + один основний .xcodeproj. Tuist або XcodeGen автогенерують .xcodeproj з конфігураційних файлів, що виключає merge-конфлікти в .pbxproj.
DI в модульній архітектурі
DI-граф не повинен бути монолітним. На Android — Hilt з @InstallIn(SingletonComponent::class) для core-залежностей та @InstallIn(ViewModelComponent::class) для feature-специфічних. Кожен feature-модуль оголошує свої @Module-класи, Hilt збирає граф у compile time. Це означає: якщо :feature:catalog не підключений в :app — його DI-модуль не компілюється і не впливає на збірку.
На iOS — ручний DI через Resolver/Swinject, або чистий factory-патерн без DI-фреймворку. В модульному застосунку DI-фреймворк на Swift менш критичний: залежності передаються через ініціалізатори, а Composition Root в :app збирає все разом.
Чому модульна архітектура краща за моноліт?
Модульний застосунок економить до 40% бюджету на розробку в довгостроковій перспективі (за даними нашого досвіду). Порівняйте: в моноліті одна помилка в shared-коді може паралізувати всю команду на день, а в модульному застосунку проблема локалізована. Google I/O демонстрував, що Play Feature Delivery дозволяє скоротити розмір базового APK на 30–50%.
Dynamic Delivery та опціональні модулі
Android дозволяє доставляти feature-модулі на вимогу через Play Feature Delivery (колишній Dynamic Delivery). Користувач завантажує базовий застосунок, важкі фічі (AR-примірка, офлайн-карти) — при першому зверненні. SplitInstallManager керує завантаженням, SplitCompatActivity — активація. На iOS аналога немає — App Clips закривають іншу нішу.
| Порівняння |
Моноліт (одна команда) |
Модульне (6 команд) |
| Час повної збірки Android |
7 хвилин |
4 хвилини (з Build Cache) |
| Перезбірка одного модуля |
7 хвилин |
45–90 секунд |
| Merge-конфліктів на місяць |
15–20 |
2–5 |
| Можливість паралельної роботи |
Обмежена |
Повна |
Кейс. Рітейл-застосунок: 6 команд, 80+ екранів, Android + iOS. До модуляризації: повна збірка Android — 7 хвилин, iOS — 11 хвилин. Після розбивки на 14 модулів з налаштованим Build Cache: інкрементальна збірка при зміні одного feature-модуля — 45–90 секунд. Merge-конфлікти в загальному коді знизилися приблизно втричі. Паралельна розробка фіч без блокувань між командами. Економія часу розробки оцінюється в 30–50% на довгій дистанції. Середній бюджет модуляризації для проекту такого розміру визначається після аудиту.
Що входить в роботу?
| Deliverable |
Опис |
| Аудит архітектури |
Аналіз поточної кодової бази, виявлення залежностей, циклічних посилань, вузьких місць збірки |
| Проектування модульної схеми |
Діаграма модулів, інтерфейси, roadmap міграції |
| Реалізація core-модулів |
Створення сітки core-шарів (network, storage, ui, auth) |
| Міграція фіч |
Кожна фіча виділяється в свій модуль зі збереженням працездатності |
| Налаштування збірки |
Build Cache, Configuration Cache, CI/CD інкрементальна збірка |
| Документація |
README модулів, схема навігації, конвенції іменування |
| Навчання команди |
Воркшопи з модульної розробки, код-рев'ю на перших етапах |
| Підтримка на старті |
2–4 тижні пост-релізної підтримки, виправлення неузгодженостей |
Як проходить модуляризація: покроково
- Аналіз поточної архітектури — виявлення залежностей та циклів.
- Проектування модульної схеми — діаграма модулів та інтерфейсів.
- Реалізація core-модулів — сітка core-шарів (network, storage, ui).
- Міграція фіч — кожна фіча виділяється в свій модуль.
- Налаштування збірки — Build Cache, Configuration Cache, CI/CD.
- Тестування та документування — тести модулів, схема навігації, README.
- Підтримка та навчання команди — код-рев'ю, воркшопи з модульної розробки.
Терміни та вартість
| Розмір проекту |
Орієнтовні терміни |
| 20–40 екранів, 1–2 команди |
4–8 тижнів |
| 50–100 екранів, 3–5 команд |
2–4 місяці |
| 100+ екранів, складні залежності |
4–8 місяців |
Вартість розраховується індивідуально після аудиту архітектури та залежностей існуючого коду. Замовте аудит — ми оцінимо ваш проект і запропонуємо оптимальний план модуляризації. Зателефонуйте або напишіть нам, щоб обговорити деталі. Дізнайтеся, як модуляризація може прискорити вашу розробку, — отримайте консультацію сьогодні.
Типові помилки при самостійній модуляризації
Circular dependencies. :feature:profile імпортує :feature:orders, :feature:orders імпортує :feature:profile. Gradle не збере — circular dependency. Рішення: винести загальні моделі в :core:domain або :shared:models, обидві фічі залежать тільки від нього.
Занадто дрібні модулі. :core:extensions з 5 extension-функціями — overhead без вигоди. Раціональний мінімум: модуль виправданий, якщо його можна міняти незалежно і він не залежить від модулів, які міняються частіше.
Resource ID конфлікти. На Android при об'єднанні ресурсів декількох модулів виникають колізії імен. Конвенція: префікс модуля для всіх ресурсів (catalog_item_card_background, не просто item_card_background). R class isolation через android.nonTransitiveRClass=true в gradle.properties — обов'язковий для великих проектів.
Чому варто довірити модуляризацію професіоналам?
Ми маємо більше 7 років досвіду в мобільній розробці та реалізували більше 30 проектів з модульною архітектурою. Гарантуємо плавний перехід без простоїв команди. Сертифікати Apple Developer та Google Associate Android Developer підтверджують нашу кваліфікацію. Зв'яжіться з нами для консультації — допоможемо вам прискорити розробку та знизити cost of change.
MVVM, Clean Architecture, BLoC, VIPER, TCA: проєктуємо архітектуру під ключ
Додаток зібрано в одному ViewController на 2000 рядків. Мережеві виклики, бізнес-логіка, оновлення UI — все в одному місці. Додати нову фічу без регресії складно, написати тест неможливо. Це не «поганий код» — це відсутність архітектури. І це трапляється частіше, ніж можна очікувати, навіть у production-додатках з мільйоном користувачів.
Ми проєктуємо архітектуру під ключ: від вибору паттерну до повної структури проєкту з тестами та документацією. За 7–10 днів отримуєте чистий модульний код, готовий до масштабування. Ваша команда зможе додавати нові фічі без ризику зламати існуючі, а тести покриють ключову логіку вже на старті. Архітектурні паттерни в мобайлі вирішують одне завдання: відокремити UI від логіки так, щоб кожна частина була тестованою та замінною.
MVVM — базовий паттерн
Model-View-ViewModel — стандарт для iOS (SwiftUI + Combine/async, UIKit + Combine) та Android (Jetpack ViewModel + StateFlow + Compose). ViewModel містить стан UI та бізнес-логіку. View лише відображає стан і передає наміри користувача до ViewModel. Model — дані та їх джерело.
Ключове правило: ViewModel не знає про UIKit або Android View-класи. Немає імпортів UIKit, немає Context-залежностей (крім Application context через Hilt). Це гарантує тестованість: ViewModel тестується як чистий Kotlin/Swift-код без Android Instrumented Test.
MVVM закриває 70% потреб. Інші 30% — де потрібна строга ізоляція фіч, масштабування команди, складний flow управління станом.
Clean Architecture — коли MVVM недостатньо
Додає шари поверх MVVM:
Domain-шар — бізнес-логіка, незалежна від платформи. UseCase (або Interactor) містить одне бізнес-правило: GetUserOrdersUseCase, PlaceOrderUseCase. Залежить лише від інтерфейсів (protocol/interface), не від конкретних реалізацій.
Data-шар — реалізація репозиторіїв. OrderRepositoryImpl реалізує OrderRepository з domain. Знає про Retrofit, Room, UserDefaults. ViewModel не знає, звідки дані — з мережі чи кешу.
Presentation-шар — ViewModel + View. Знає про Domain, не знає про Data.
Dependency rule: залежності спрямовані лише всередину. Domain не залежить ні від чого. Data та Presentation залежать від Domain.
Presentation → Domain ← Data
Це дає можливість підміняти реалізацію: тест використовує in-memory репозиторій замість мережевого, інтерфейс залишається тим самим.
Практичне зауваження: Clean Architecture додає файли та шари. Для невеликого додатку це overhead. Виправдано від ~15 фіч та при команді 3+ розробників.
BLoC для Flutter — передбачуваний потік станів
BLoC (Business Logic Component) — стандартний паттерн у Flutter-спільноті. Бібліотека flutter_bloc реалізує його через два типи: Bloc (Event → State) та Cubit (State без Events, лише методи).
Bloc обробляє Event та емітує новий State через on<EventType> хендлери. Стан імутабельний — новий об'єкт на кожну зміну. BlocBuilder перемальовує лише ту частину дерева, де змінився state.
// Подія
alias CartEvent {}
class AddItemToCart extends CartEvent {
final String productId;
AddItemToCart(this.productId);
}
// Стан
alias CartState {}
class CartLoaded extends CartState {
final List<CartItem> items;
CartLoaded(this.items);
}
// Bloc
class CartBloc extends Bloc<CartEvent, CartState> {
CartBloc(this._cartRepository) : super(CartLoaded([])) {
on<AddItemToCart>(_onAddItem);
}
Future<void> _onAddItem(AddItemToCart event, Emitter<CartState> emit) async {
final current = state as CartLoaded;
final updated = await _cartRepository.addItem(event.productId);
emit(CartLoaded(updated));
}
}
Перевага BLoC — тестованість. blocTest з bloc_test пакету дозволяє перевірити: при такому-то Event, з таким-то початковим State, BLoC має емітувати такий-то State. Без UI, без моків для Flutter-фреймворку.
VIPER — для великих iOS-проєктів
VIPER (View, Interactor, Presenter, Entity, Router) — найбільш строгий поділ обов'язків для iOS. Кожен компонент має протокол та конкретну реалізацію.
- View — лише UI, делегує все Presenter
- Interactor — бізнес-логіка, робота з мережею та даними
- Presenter — посередник між View та Interactor, форматує дані для View
- Entity — моделі даних (чисті структури)
- Router — навігація між модулями
Кожен модуль (екран або фіча) — окремий VIPER-модуль. Це виключає coupling між фічами та дозволяє великим командам працювати паралельно без конфліктів.
Ціна: багато файлів, багато протоколів. Шаблонний код генерується через Sourcery або кастомні Xcode-шаблони. VIPER виправданий для додатків з 10+ розробниками та 50+ екранами.
TCA (The Composable Architecture)
TCA від Point-Free — більш сучасна альтернатива VIPER для iOS/macOS. Основні концепції: State (імутабельний стан фічі), Action (всі можливі події), Reducer (State + Action → новий State + Effect), Store (зберігає State, обробляє Actions).
Scope дозволяє composable будувати великі фічі з маленьких: батьківський Reducer делегує частину State дочірньому. Кожна фіча тестується ізольовано через TestStore з точним контролем над Effects.
TCA має круту криву навчання, але дає передбачуваність, яку складно отримати іншим способом: кожна зміна стану — явний Action з конкретним джерелом.
Як обрати архітектуру мобільних додатків?
Оцінимо проєкт за 1 день — підберемо архітектуру з урахуванням розміру команди, платформи та планів зростання.
| Паттерн |
Платформа |
Команда |
Коли обирати |
| MVVM |
iOS, Android, Flutter |
1–5 |
Стартовий стандарт, MVP, невеликі проєкти |
| MVVM + Clean |
iOS, Android |
3–10 |
Середні проєкти, тестованість критична |
| BLoC |
Flutter |
2–8 |
Flutter з передбачуваним state management |
| VIPER |
iOS |
5–20 |
Великі iOS-проєкти, модульна архітектура |
| TCA |
iOS/macOS |
3–15 |
Сувора тестованість, Swift Concurrency |
Універсальної відповіді немає. Архітектуру обирають під розмір команди, вимоги до тестованості та горизонт підтримки додатку.
Що входить у нашу роботу
Процес складається з кількох кроків:
-
Аудит поточної архітектури (якщо додаток вже існує) — виявимо вузькі місця та регресійні зони.
-
Проєктування модульної структури з чіткими межами шарів та правилами залежностей.
-
Створення каркасу проєкту (Scaffold) з впровадженням DI, організації папок та налаштування лінтерів.
-
Написання юніт-тестів для шару домену та ViewModel — мінімум 80% покриття ключових use case.
-
Підготовка документації — архітектурні діаграми, README з правилами модифікації коду, інструкція для онбордингу нових розробників.
-
Передача робочого репозиторію з CI-пайплайном (GitHub Actions / Bitrise), налаштованим запуском тестів та статичним аналізом.
Все це входить у вартість проєктування. Додатково — підтримка на етапі впровадження: консультації команди, code review перших pull request.
Що відбувається без архітектури
Типовий сценарій через 18 місяців без архітектури: 40% часу розробки йде на дебаг регресій. Новий розробник розбирається в коді тиждень перед тим, як зробити перший PR. Тести не пишуться, «тому що складно мокувати». Додавання нової фічі вимагає розуміння половини кодової бази.
Вибір архітектури на старті — це інвестиція з поверненням через 3–6 місяців. За нашими даними, правильно спроєктована архітектура з MVVM + Clean дає в 3 рази менше регресій порівняно з монолітним ViewController. А витрати на її впровадження окупаються за 2–3 спринти. Середня економія на усуненні дефектів після впровадження — до 40% часу, що для команди з п'яти осіб може означати понад 10 000 доларів на рік.
Згідно з рекомендаціями Apple щодо проєктування додатків, розділення обов'язків — ключовий фактор стійкості коду.
Чому варто довірити архітектуру професіоналам?
Неправильний вибір паттерну на старті веде до переписування половини коду через рік. Ми бачили десятки проєктів, де спроба заощадити на архітектурі оберталася багатомісячним рефакторингом. У нас за плечима 10+ років комерційної розробки, досвід роботи з додатками від 1 до 50 розробників, понад 100 успішно реалізованих проєктів. Ми допомагаємо уникнути типових помилок:
- Overengineering для простого MVP (призначаємо MVVM, а не VIPER).
- Відсутність dependency injection — підключаємо Hilt/Koin/Dagger вже на старті.
- Ігнорування тестованості — закладаємо протоколи/інтерфейси з першого коміту.
Зв'яжіться з нами через Telegram або email — отримайте безкоштовну оцінку вашого проєкту та рекомендацію щодо оптимальної архітектури. Замовте проєктування вже сьогодні — і за тиждень стартуйте з чистим, масштабованим кодом.
Додаткова таблиця: порівняння витрат на впровадження
| Паттерн |
Час проєктування |
Кількість файлів на 1 екран |
Час написання тестів |
| MVVM |
2–3 дні |
5–7 |
1 день |
| MVVM + Clean |
4–5 днів |
10–12 |
2 дні |
| BLoC |
3–4 дні |
6–8 |
1.5 дня |
| VIPER |
5–7 днів |
12–15 |
2.5 дня |
| TCA |
5–6 днів |
8–10 |
2 дні |
Час вказано для команди з 2–3 розробників. З нашим шаблоном (generator) стартовий каркас готовий за 1 день незалежно від обраного паттерну.