なぜiOS/Android/Webを1つのFirestoreで同期しているか——E-Spaceのマルチプラットフォーム設計判断

E-SpaceはSwiftUI(iOS)、Jetpack Compose(Android)、Next.js(Web)の3つのUIスタックで動いている。だがデータの同期は1つのFirestoreだけで完結している。リアルタイムリスナーの対称性・セキュリティルール集約・オフライン同期の3軸と、ベンダーロックイン・コスト爆発・書き込みスループット上限という3つの反証を交えて、その設計判断を記録する。

Firestoreを選んだ主な理由は3つだ——(1)全プラットフォームに対称なリアルタイムリスナーが存在すること、(2)データモデルとセキュリティルールを1箇所で管理できること、(3)オフライン同期の実装コストが低いこと。ただし、この判断が誰にでも当てはまるとは言えない。反証から先に出す。


反証データ:Firestoreが万能でない3つのファクト

反証①:ベンダーロックインは現実のリスク

FirestoreのクエリモデルはSQLと非互換で、PostgreSQLへのデータエクスポートは手動かつ変換ロスを伴う(encore.dev、Firebase Alternatives 2026)。Supabaseなどのオープンソース代替が2025〜2026年にシェアを拡大しており、大規模では通常Firestoreより安価だ。「今Firestoreを選ぶ」は「将来の移行コストを受け入れる」と同義だと認識している。

反証②:read/write課金のコスト爆発リスク

Firestoreはdocumentごとの読み書き課金だ。read-heavyな画面設計では、100K DAUで年間$4,380規模のコスト差が出ることがある(Firemap.dev、Firestore Pricing Explained 2026)。Budget Alertsを設定しない限り利用上限がなく、機能追加のたびに請求が静かに増える。

反証③:1ドキュメントあたり1 write/秒の上限

"Firestore limits a single document to ~1 write/second. Use distributed counters or sharded aggregation instead."

— Firebase 公式ドキュメント (firebase.google.com/docs/firestore/quotas)

多ユーザーが同一ドキュメントを高頻度更新するシナリオ(リアルタイム集計・ライブカウンタ等)ではDistributed Countersが必要だ。LWW(last-write-wins)はクライアントクロック依存のため、複数端末からの同時編集では競合が非決定的になりうる(moldstud.com、Firebase Data Sync Challenges)。


理由1:リアルタイムリスナーが全プラットフォームで対称

FirestoreのonSnapshotリスナーはiOS/Android/Webで同じセマンティクスを持つ。

iOS(Swift addSnapshotListener)、Android(Kotlin addSnapshotListener)、Web(TypeScript onSnapshot)が同じFirestoreコレクションを購読し、どのプラットフォームで書き込みが発生しても全端末にリアルタイムで変更が届く構成を示した図
// iOS (Swift)
db.collection("class_rooms").document(classRoomId)
  .collection("schedules")
  .addSnapshotListener { snapshot, error in
    guard let docs = snapshot?.documents else { return }
    schedules = docs.compactMap { try? $0.data(as: Schedule.self) }
  }
// Android (Kotlin)
db.collection("class_rooms").document(classRoomId)
  .collection("schedules")
  .addSnapshotListener { snapshot, error ->
    if (snapshot == null) return@addSnapshotListener
    schedules = snapshot.documents.mapNotNull { it.toObject(Schedule::class.java) }
  }
// Web (TypeScript)
onSnapshot(
  collection(db, "class_rooms", classRoomId, "schedules"),
  (snapshot) => {
    schedules = snapshot.docs.map((d) => d.data() as Schedule)
  }
)

3つの実装は構造的に同じだ。リスナーを登録すれば、Firestoreがデータ変化をプッシュする。「どのプラットフォームでスケジュールを更新したか」に関わらず、他の全端末に変更が届く。この対称性が、プラットフォームごとに同期ロジックを書き分けるコストをゼロにしている。クロスプラットフォームフレームワークの採用は開発時間を最大40%削減すると報告されており(neontri.com 2026)、Firestoreの対称APIがその削減を下支えしている。


理由2:セキュリティルールとデータモデルを1箇所で管理

Firestore Security Rulesは firestore.rules 1ファイルで全プラットフォームに適用される。

// firestore.rules(説明用に簡略化した例)
match /class_rooms/{classRoomId} {
  allow read, write: if request.auth != null &&
    request.auth.uid in resource.data.adminUserIds;

  match /schedules/{scheduleId} {
    allow read: if request.auth != null &&
      request.auth.uid in
        get(/databases/$(database)/documents/class_rooms/$(classRoomId))
          .data.joinedUserIds;
    allow write: if request.auth != null &&
      request.auth.uid in
        get(/databases/$(database)/documents/class_rooms/$(classRoomId))
          .data.adminUserIds;
  }
}

「教室メンバーだけが読める・管理者だけが書ける」というルールを1行変えれば、iOS/Android/Web全てに同時に反映される。プラットフォームごとにAPIのアクセス制御を調整するコストはない。

データモデルも同様だ。Schedule 構造体(iOS)、Schedule データクラス(Android)、Schedule TypeScript型(Web)は同じFirestoreドキュメント構造から生成される。フィールドを追加する際は型定義を3箇所修正するが、DBスキーマは変えなくてよい。


理由3:オフライン同期がデフォルト動作する

塾の現場では、教室端末がWi-Fiの切れ目で一時オフラインになることがある。FirestoreはiOS/Androidでオフライン永続化をデフォルトで有効にしており、接続が復帰した時点で自動同期する(firebase.google.com/docs/firestore)。Webもオプトインで同じAPIから有効にできる。

オフライン対応を自前で実装するコストは高い。キャッシュ管理・再同期ロジック・競合解決の全てを書くと、それだけでエンジニア数週間分の工数になる。Firestoreはこれをバックエンドとして隠蔽する。2025年初頭時点でFirebaseのアクティブドメインは42,377(2020年比13.7倍増)で(technologychecker.io 2026)、この隠蔽コストが普及の主因の一つだろう。


なぜ反証がE-Spaceには当てはまらないのか

3つの反証は「大規模コンシューマーアプリ」または「高頻度更新が必要なリアルタイムアプリ」を前提にしている。E-Spaceは3点とも該当しない。

反証①(ベンダーロックイン): 現時点のデータ量は小中規模の塾数百教室分だ。移行コストは将来発生しうるが、今の段階でその工数をかける意思決定は正当化されない。規模感が変わった時点で判断しても間に合う。

反証②(コスト爆発): E-Spaceはスケジュール管理SaaSだ。1教室あたりのdocument reads/dayは限定的で、チャット・ライブフィードのようなread爆発シナリオには当たらない。Budget Alertsで監視しており、請求は予測可能な範囲に収まっている。

反証③(1 write/秒上限): 授業スケジュールの更新は非同時的で低頻度だ。複数管理者が同一スケジュールを同時編集する競合は実運用でほぼ発生しない。カウンタ系データも使っていない。


結論

E-SpaceがFirestoreを選んだのは、3プラットフォームのリスナーAPIが対称であり、セキュリティルールを1箇所で管理でき、オフライン同期がデフォルトで動くからだ。ベンダーロックイン・コスト爆発・1 write/秒上限という3つのリスクは現実だが、小中規模の塾向けSaaSという今のフェーズには当てはまらない。この判断はE-Spaceの規模と要件があって成立する。規模が変わればデータベース選択も変わりうる。

E-Space

E-Spaceのエンジニアチームへ

iOS(SwiftUI / Swift 6.2)・Android(Kotlin / Jetpack Compose)・Web(Next.js)の3プラットフォームを少人数で動かしています。こういう判断を一緒にしていく仲間を探しています。

採用情報を見る

関連記事

2026.06.23 / 運営ノウハウ

塾管理アプリのマルチデバイス同期 — iPhone・Android・PCで同じ予定を

2026.08.18 / 技術ブログ

サブスク課金をWebだけに寄せた理由——iOS/Androidのアプリ内課金を捨てた設計と事業判断