モバイルアプリの利用シーンは、常に安定したネットワーク接続があるとは限りません。地下鉄での移動中、山間部での作業、海外旅行中のローミング節約。あるいは単純にサーバーが一時的にダウンしている場面もあります。オフラインファースト設計とは、ネットワーク接続がない状態でもアプリが正常に動作し、接続が復帰した際にデータを同期する設計思想です。
筆者がフィールドワーカー向けの業務アプリを開発した際、この設計思想の重要性を痛感しました。工事現場や農場など、電波状況が不安定な環境で使われるアプリにとって、オフライン対応は「あると便利な機能」ではなく「必須要件」だったのです。
オフラインファースト設計の核心は、ローカルデータベースをSingle Source of Truth(信頼できる唯一の情報源)として扱うことです。すべての読み書きはまずローカルDBに対して行い、サーバーとの同期はバックグラウンドで非同期に処理します。
この設計により、以下のメリットが得られます。
モバイルアプリで利用可能なローカルDBには複数の選択肢があります。それぞれの特徴を理解した上で選定することが重要です。
AndroidではRoom、iOSではCore DataやGRDBがSQLiteのラッパーとして利用されています。リレーショナルなデータモデルに適しており、複雑なクエリも記述可能です。長い歴史があるため、パフォーマンスの最適化手法や運用ノウハウも豊富に蓄積されています。
オブジェクト指向のデータベースで、データの変更をリアクティブに監視できる機能が強力です。MongoDB Realmと組み合わせることで、サーバーとの自動同期機能も利用できます。ただし、ベンダーロックインのリスクは考慮すべきです。
React Native向けに設計されたデータベースで、SQLiteの上に構築されています。遅延読み込みとリアクティブなデータ取得を組み合わせた設計で、大量データでもUIのパフォーマンスを維持できます。
オフラインファースト設計の最難関は同期戦略です。ここでの設計判断が、システム全体の信頼性とユーザー体験を左右します。
各レコードに更新日時を持たせ、最終同期日時以降に変更されたレコードだけをやり取りする方法です。実装がシンプルですが、デバイス間の時刻のずれや、同時編集の検知が困難という制約があります。
各デバイスが独立したバージョンカウンターを持ち、どのデバイスでいつ変更が行われたかを追跡します。より正確な変更追跡が可能ですが、実装の複雑度は増します。
データ構造自体に競合解決のロジックを組み込むアプローチです。数学的に競合が発生しないことが保証されるため、マージ処理が不要になります。テキストの共同編集のようなユースケースに特に有効ですが、すべてのデータ型に適用できるわけではありません。
複数のデバイスが同じデータをオフラインで編集した場合、競合が発生します。この解決方法には複数のパターンがあります。
実務では、データの重要度に応じてパターンを使い分けるのが現実的です。設定情報はLast Write Winsで十分ですが、業務データはフィールドレベルマージ、金銭に関わるデータは手動解決、という具合です。
同期処理は失敗する可能性が常にあるため、以下の対策が重要です。
オフラインファースト設計で最も過小評価されがちなのは、テストの難しさです。オフライン→オンライン→オフラインの遷移パターン、部分的な接続(タイムアウトが頻発する状況)、複数デバイスでの同時編集といったシナリオを網羅的にテストする必要があります。
また、ローカルDBのマイグレーション戦略も事前に設計しておくべきです。アプリのアップデートでスキーマが変わった場合、既存のローカルデータを安全に移行する仕組みがないと、ユーザーのデータが失われるという最悪の事態を招きます。
オフラインファースト設計は初期の実装コストが高いですが、ユーザー体験とデータの信頼性において大きなリターンをもたらします。特にフィールドワーカー向けアプリやグローバル展開を見据えたサービスでは、初期段階から検討する価値があります。