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)
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の規模と要件があって成立する。規模が変わればデータベース選択も変わりうる。