Розробка мікросервісної архітектури бекенду мобільного застосунку
Перехід від моноліту до мікросервісів — не данина моді, а вирішення конкретних операційних проблем. Коли команда розростається до 15+ осіб, кожен коміт у спільну кодову базу загрожує конфліктами, релізний цикл розтягується на два тижні, а одна «гаряча» фіча (стрімінг, обробка платежів) потребує окремого масштабування — моноліт стає вузьким місцем. Ми спроектували та впровадили мікросервісну архітектуру для 20+ мобільних застосунків із навантаженням до 2 млн MAU. Наш досвід 7 років на ринку та сертифікації AWS і Kubernetes підтверджують експертизу. Правильна декомпозиція знижує latency кінцевих точок на 60–80% і скорочує час виходу нових фіч з двох тижнів до двох днів.
Але мікросервіси — не срібна куля. Більш ніж 40% міграцій закінчуються створенням розподіленого моноліту, який працює гірше за вихідний. Щоб уникнути цього, потрібно розуміти межі застосовності та платити за оверхед усвідомлено. У цій статті розберемо, які антипатерни вбивають мікросервісні проєкти, як правильно декомпозувати домен і що входить у нашу роботу з проектування архітектури.
Де мікросервіси створюють більше проблем, ніж вирішують
Почнемо з антипатернів, тому що саме сюди йде більшість бюджетів.
Distributed monolith. Розбили моноліт на 15 сервісів, але кожен запит мобільного клієнта синхронно чекає ланцюжок викликів: API Gateway → UserService → OrderService → ProductService → PaymentService. Один сервіс лагає — весь запит висить. Це не мікросервісна архітектура, це моноліт через HTTP з додатковим latency overhead. Ознака: сервіси не можуть працювати незалежно, деплой одного потребує координації з іншими.
Grapevine data consistency. OrderService синхронно викликає InventoryService, отримує 200 OK, але між перевіркою залишку та списанням інший запит встиг зайняти останній товар. Race condition на рівні розподіленої системи. Рішення — Saga pattern: або choreography (події через Kafka/RabbitMQ), або orchestration (Temporal, Apache Camel).
Overhead на маленьких командах. Три розробники, п'ять сервісів — кожен деплой потребує оновлення п'яти Docker-образів, п'яти Helm-чартів, п'яти наборів змінних середовища. Velocity падає, помилки конфігурації зростають. Для команди до 8 осіб модульний моноліт (Modular Monolith) або monolith-first з подальшою декомпозицією — чесніше.
Як правильно декомпозувати домен? (Domain-Driven Design)
Bounded Context — основний принцип: кожен сервіс володіє своїми даними і не читає чужу БД напряму. Типова декомпозиція для e-commerce мобільного застосунку:
| Сервіс |
Відповідальність |
БД |
| user-service |
Реєстрація, профіль, аутентифікація |
PostgreSQL |
| catalog-service |
Товари, категорії, пошук |
PostgreSQL + Elasticsearch |
| order-service |
Замовлення, статуси, історія |
PostgreSQL |
| payment-service |
Платіжні методи, транзакції |
PostgreSQL |
| notification-service |
Push, email, SMS |
Redis + PostgreSQL |
| media-service |
Завантаження та обробка медіа |
S3 + PostgreSQL |
Навіщо потрібен API Gateway для мобільного застосунку?
Мобільний клієнт ніколи не знає про топологію сервісів. Один хост, один TLS-сертифікат. Gateway бере на себе: аутентифікацію JWT (не дублюємо в кожному сервісі), rate limiting, роутинг за версіями API, трансформацію запитів.
Технології: Kong (production-proven, плагіни для auth/rate limit/logging), AWS API Gateway якщо інфраструктура в AWS, Traefik для Kubernetes-native рішення. Для команд, які хочуть кастомну логіку — custom Gateway на Go (BFF — Backend for Frontend), особливо якщо мобільний клієнт потребує агрегації даних з кількох сервісів в одну відповідь.
Як асинхронна комунікація покращує продуктивність?
Синхронні виклики між сервісами — тільки там, де результат потрібен негайно (перевірка ліміту перед платежем). Все інше — через message broker.
Apache Kafka для високонавантажених сценаріїв: event sourcing, аудит-лог, стрімінг метрик. Топік order.created — підписані notification-service (відправить push), analytics-service (оновить метрики), loyalty-service (нарахує бали). Кожен незалежно, без coupling.
RabbitMQ для task queue: транскодування відео, генерація PDF, відправка email. Простіше в операційному плані, достатньо для більшості мобільних застосунків.
Кейс: платформа для стрімінгу фітнес-контенту, 200 000 MAU. Вихідний моноліт на Node.js почав давати timeout на /api/workouts/start — там синхронно викликалися: запис у лог, оновлення прогресу, інкремент лічильника переглядів, перевірка ачівок. Декомпозиція: workout-service публікує подію workout.started в Kafka, інші сервіси обробляють асинхронно. Latency endpoint: з 800ms до 40ms — покращення в 20 разів.
Як організувати observability в мікросервісах?
У мікросервісному середовищі без observability ви сліпі. Обов'язковий мінімум:
- Distributed tracing — Jaeger або Zipkin з OpenTelemetry SDK у кожному сервісі. Коли мобільний клієнт скаржиться на повільну відповідь, trace показує, який саме сервіс винен.
- Centralized logging — ELK Stack (Elasticsearch + Logstash + Kibana) або Loki + Grafana. Correlation ID пробрасывается через всі сервіси в заголовках (
X-Trace-ID).
- Service mesh — Istio або Linkerd для mTLS між сервісами, circuit breaker, retry, timeout на рівні інфраструктури, без коду.
Як Circuit Breaker захищає мобільного клієнта?
Якщо payment-service деградує, order-service має швидко повернути fallback, а не чекати тайм-ауту 30 секунд. Resilience4j (Java), Polly (.NET), go-circuitbreaker — circuit breaker розмикається після N послідовних помилок, швидко повертає 503, через 30 секунд пробує знову. Мобільний клієнт отримує відповідь за 100ms, а не тайм-аут.
Порівняння підходів до міжсервісної взаємодії
| Параметр |
Синхронне (REST/gRPC) |
Асинхронне (Kafka/RabbitMQ) |
| Latency для клієнта |
Зазвичай нижче, якщо ланцюжок короткий |
Може бути вище, але більш передбачувано |
| Масштабування |
Складніше — потрібно балансувати кожен сервіс |
Простіше — брокер автоматично обробляє піки |
| Стійкість до відмов |
Низька — один збій блокує все |
Висока — відмова споживача не впливає на інших |
| Складність налагодження |
Вище — потрібно трасувати кожен виклик |
Нижче — події можна повторно відтворювати |
Вибір між Kafka та RabbitMQ: Kafka краще при високих навантаженнях (>100k повідомлень/с) і для event sourcing. RabbitMQ простіше в налаштуванні та підтримці, підходить для task-queues і малих/середніх навантажень (до 50k повідомлень/с). Для більшості мобільних застосунків з MAU до 1 млн достатньо RabbitMQ.
Процес впровадження
Міграція моноліту в мікросервіси — не «переписуємо все разом». Патерн Strangler Fig: новий функціонал — окремий сервіс, старий — поступово вирізається з моноліту. Починаємо з найменш зв'язаних доменів (зазвичай сповіщення, медіа, аналітика).
| Етап |
Терміни |
| Проектування архітектури + базова інфраструктура (Gateway, брокер, моніторинг) |
4–6 тижнів |
| Повна декомпозиція продуктового моноліту в 5–8 сервісів з CI/CD |
16–24 тижні |
Що входить у роботу
- Документація архітектури: діаграми C4, опис API, схеми data flow.
- Вибір та налаштування інфраструктури: Kubernetes, CI/CD, моніторинг.
- Розробка core-сервісів (Auth, API Gateway, брокери).
- Міграція першого домену (пілотний проєкт).
- Навчання команди: DDD, патерни, робота з інфраструктурою.
- Підтримка на етапі стабілізації (2–3 місяці).
Якщо ви шукаєте досвідчену команду для проектування мікросервісної архітектури під мобільний застосунок — зв'яжіться з нами. Ми оцінимо ваш проєкт, підберемо оптимальний стек і запропонуємо план поетапної міграції. Отримайте консультацію безкоштовно.
Примітка: для глибокого розуміння патернів рекомендуємо вивчити Saga pattern та Circuit Breaker на Wikipedia.
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 день незалежно від обраного паттерну.