Зв'язок — це привілей, а не гарантія у військових польових операціях. Глушники GPS, маскування рельєфом, щільна міська забудова, підводні операції та навмисне радіомовчання — усе це дає один результат: ваш застосунок має функціонувати без будь-якого мережевого доступу. Це не крайній випадок для витонченої обробки — це основний операційний режим, для якого мають проєктуватися тактичні мобільні застосунки.
Офлайн-перш — це філософія проєктування, а не функція. Це означає, що застосунок був спроектований з нуля з припущенням про відсутність зв'язку, а мережева синхронізація є доповненням, а не передумовою. Практичний наслідок — архітектурний: усі дані, необхідні застосунку, мають зберігатися на пристрої, усі дії, що виконує користувач, мають записуватись локально, а рушій синхронізації повинен узгоджувати локальний стан із сервером, коли зв'язок врешті-решт відновлюється.
Чому офлайн-режим є жорсткою вимогою
Режим відмови застосунку з пріоритетом підключення у відключеному середовищі не є витонченим. Коли застосунок втрачає з'єднання з сервером, він зазвичай показує помилку, вимикає функціональність або відображає застарілі дані без вказівки їхнього віку. Жодна з цих моделей поведінки неприйнятна в тактичному контексті. Оператор, який втрачає тактичну картину через те, що модем стільникового зв'язку втратив сигнал, зазнав деградації своїх операційних можливостей від програмного забезпечення, яке мало їх покращити.
Аргумент критичності даних є не менш переконливим. Під час тактичної операції події, які застосунок має записувати — доповіді про місцезнаходження, оновлення статусу, доповіді про контакт, записи про втрати — відбуваються саме в ті моменти, коли зв'язок найімовірніше відсутній. Запис цих подій на віддалений сервер у реальному часі нежиттєздатний. Вони мають захоплюватися локально та синхронізуватися пізніше. Система, яка втрачає дані через відсутність мережі під час бою, провалила своє фундаментальне завдання.
Є також безпековий аспект. У спірних електромагнітних середовищах зменшення радіовипромінювань саме по собі є тактичною вимогою. Застосунок, що постійно обмінюється з сервером, генерує радіочастотну енергію, яку можна виявити й визначити місцезнаходження. Офлайн-операція з пакетною зашифрованою синхронізацією зменшує РЧ-підпис рівня даних системи.
Локальне сховище даних: SQLite, Realm та WatermelonDB
SQLite — найширше використовувана вбудована база даних на Android та iOS. Вона зріла, добре зрозуміла і має передбачуваний профіль продуктивності. Для тактичних застосунків зі структурованими моделями даних — записи позицій, таблиці статусів підрозділів, логістичні транзакції — SQLite є надійним вибором за замовчуванням. Бібліотека Android Room забезпечує типобезпечну абстракцію Kotlin/Java над SQLite з перевіркою запитів під час компіляції, що виявляє помилки схеми до часу виконання.
Характеристики продуктивності SQLite важливо розуміти на обсягах тактичних даних. Пропускна здатність записів без Write-Ahead Logging (WAL) обмежена операціями синхронізації з диском. Увімкнення режиму WAL (PRAGMA journal_mode=WAL) значно покращує продуктивність паралельного читання та пропускну здатність записів — зазвичай у 3–5 разів для навантажень зі змішаними читаннями та записами. Для застосунків, що записують високочастотні позиційні дані (оновлення GPS з частотою 10 Гц від трекера транспортного засобу), режим WAL є обов'язковим.
Realm — це мобільна база даних, розроблена для перевищення продуктивності SQLite для зберігання об'єктних графів. Її основна перевага перед SQLite — ліниве завантаження: об'єкти Realm відображаються з пам'яті з диска, тобто ви ніколи не завантажуєте більше даних, ніж отримуєте доступ. Для застосунків, що працюють з великими об'єктними графами — повний бойовий порядок з вкладеними ієрархіями підрозділів — патерн доступу Realm може значно знизити тиск на пам'ять порівняно з завантаженням цілих результатів запитів SQLite у пам'ять.
WatermelonDB — це специфічна для React Native високопродуктивна база даних, побудована на SQLite. Її ключова особливість дизайну — ліниве спостереження: вона отримує дані лише тоді, коли UI дійсно їх потребує. Для оборонних застосунків, побудованих на React Native, WatermelonDB забезпечує добре структуровану офлайн-основу.
Стратегії синхронізації: LWW, операційні перетворення, CRDT
Last-Write-Wins (LWW) — найпростіша стратегія: коли дві версії запису конфліктують, перемагає версія з пізнішою мітками часу. LWW легко реалізувати і добре працює для даних, що рідко редагуються кількома операторами одночасно — наприклад, позиції підрозділів, де лише один пристрій є авторитетним для місцезнаходження кожного підрозділу. Її режим відмови — тиха втрата даних.
Операційні перетворення (OT) вирішують проблему, яку не може вирішити LWW: паралельні редагування одного запису. OT трансформує вхідні операції з урахуванням операцій, що вже були застосовані локально, отримуючи результат, що включає обидві зміни. Для тактичних застосунків OT цінний, коли кілька операторів можуть редагувати один і той самий документ або запис.
CRDT (Conflict-free Replicated Data Types) — математично строге рішення для синхронізації розподіленого стану. CRDT — це структура даних, розроблена так, щоб будь-який набір паралельних оновлень міг бути об'єднаний без конфліктів, незалежно від порядку їх отримання. Бібліотеки Automerge та Yjs надають готові до виробництва реалізації CRDT, які можуть бути вбудовані в мобільні застосунки.
MBTiles для офлайн-карт
Офлайн-карти в тактичних застосунках майже повсюдно постачаються у форматі MBTiles — відкрита специфікація, що пакує тайли карти в базу даних SQLite. Схема проста: таблиця tiles із стовпцями zoom_level, tile_column, tile_row та tile_data, плюс таблиця metadata, що записує ім'я карти, формат, межі та мінімальний/максимальний рівні масштабування.
Стратегії часткового оновлення для офлайн-карт вирішують критичну операційну проблему: як оновити конкретну область офлайн-карти без необхідності повторного завантаження всього пакету карти? Відповідь — дельта-пакети — файли MBTiles, що містять лише тайли, які змінилися з часу останньої версії. Процес оновлення об'єднує дельта-пакет у головну базу даних MBTiles пристрою, використовуючи семантику INSERT OR REPLACE SQLite.
Фонова синхронізація при відновленні зв'язку
Рушій синхронізації запускається у фоні та має обробити всю складність синхронізації потенційно годин офлайн-активності при відновленні зв'язку. Три принципи проєктування керують його реалізацією.
Експоненціальне відкладення з джиттером. При збої синхронізації повторюйте спробу з експоненціально зростаючою затримкою плюс випадковий джиттер. Починаючи з 30 секунд, подвоюючи кожен збій до максимуму 30 хвилин, з ±25% джиттером для запобігання синхронізованим штормам повторних спроб.
Черга пріоритетів. Не всі дані однаково важливі. Доповіді про позиції для поточних операцій мають синхронізуватися перед історичними журналами. Термінові зміни статусу (запит CASEVAC, доповідь про контакт) мають синхронізуватися перед рутинними доповідями.
Ідемпотентні операції. Кожна операція синхронізації має бути ідемпотентною — застосування двічі має давати той самий результат, що й застосування один раз. Це вимагає призначення стабільних UUID усім записам при створенні та використання семантики upsert, а не сліпих вставок на стороні сервера.
Ключовий висновок: Рушій синхронізації — найскладніший і найбільш схильний до збоїв компонент тактичного застосунку з офлайн-пріоритетом. Заплануйте бюджет відповідно — наївна реалізація пошкодить дані у виробництві в реальних операційних умовах. Тестуйте з симульованими мережевими розподілами тривалістю 12–24 години, а не лише з миттєвими відключеннями.