Firebase-as-a-Store ～RxJSで作るFirebaseバックエンドのRedux～

[Firebase Advent Calendar 2016](http://qiita.com/advent-calendar/2016/firebase) の10日目です。ちきさんです。

今回のGitHubリポジトリは [ovrmrw/qiita-advent-calendar-2016-firebase-2](https://github.com/ovrmrw/qiita-advent-calendar-2016-firebase-2) です。オンラインデモは [こちら](https://ovrmrw.github.io//qiita-advent-calendar-2016-firebase-2/) です。

ちなみにAngularとRxJSで書いています。

---

豆知識です。Firebaseのプロジェクト数は12個ぐらいで上限を迎えますがちゃんとGoogleに申請すれば拡張してもらえますよ。それについては [ここでも](http://qiita.com/ovrmrw/items/22e76f12c814de6055e6#%E6%9C%80%E3%82%82%E3%81%8A%E6%89%8B%E8%BB%BD%E3%81%AA%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9firebase) 触れています。

---

さて本題です。

Firebase DBはいわゆる**JSONツリーの構造で**データを保管できるDBです。配列の扱いが少々微妙ですがほぼほぼJSONです。

そしてReduxはいわゆる**JSONツリーの構造で**状態を管理するStoreです。

あれ？

JSON ≠ Redux' state ...
Firebase DB's data ≠ JSON ...

JSON ≠ Redux ... Firebase ≠ JSON ...

~~Pen Pineapple ... Apple Pen ...~~

そうです。**JRFJ (JSON Redux Firebase JSON) です。**

## Reduxとは

[reactjs/redux](https://github.com/reactjs/redux)

Flux界隈で超有名な実装です。初心者にもわかりやすい概念でFluxの世界に入り込むのに適していると言えます。公式サイトは英語ですが「redux」でググれば日本語情報はいくらでも探せるでしょう。

関連してこんなものも書きました。→ [Reduxの概念をRxJSとTypeScriptで理解する ver.2](http://qiita.com/ovrmrw/items/8cca6f40d5f78909a3dc)

## JRFJ (JSON Redux Firebase JSON)

![qiita-firebase-1.png](https://qiita-image-store.s3.amazonaws.com/0/74793/58223cc2-dd3b-0ae6-b9b7-7311f9444300.png)

早い話が、

- ReduxのデータがJSON構造なら、そのままFirebaseに持っていけるよね。
- FirebaseのデータがJSON構造なら、そのままReduxに持っていけるよね。

ということです。

つまりFirebase DBをRedux Storeのバックアップ先として使いたいということなんですね。僕はこれを **Firebase as a Store** と勝手に呼んでいます。

## 何がうれしいのか

僕は普段SIer(?)なのでその中での常識感で言うと、入力中のデータが利用者の意図に反して勝手に消えてしまうのはNGなんです。入力をキャンセルするならするで、明確なキャンセル操作があった上でそうなって欲しい。

でもWebはステートレスな世界なので原則として以前の状態は知らない。だからリロードとかしただけで容易に入力中のデータが消えてしまいます。
長い入力フォームがsubmitでコケてフリーズ状態になってやむを得ずリロードしたらデータが消えてブチ切れたなんて、みなさん一度は経験してますよね。
そんなの業務システムの世界でやったら完全アウトです。

そこで **Firebase as a Store** です。ReduxのステートをFirebaseにバックアップし続けるので入力中のデータは、

- いつリロードしても
- 別のページに移動して帰ってきたとしても
- 別の端末で開いたとしても
- 次の日であっても

復元されます。
なぜならデータの主体はローカルにあるのではなくクラウド(Firebase)にあるからです。

さらに開発者が望むなら複数端末で同時に開いておいて同期することもできます。Firebase Realtime DBとしての機能を使って。

## 1. Firebase --> Redux

![qiita-firebase-2.png](https://qiita-image-store.s3.amazonaws.com/0/74793/2109275b-3734-162d-bf4e-f8a78eb743bc.png)

まずWebアプリのページをブラウザで開きます。
そしてFirebaseからデータを取得してReduxに格納します。

これにより初期状態が作られます。初回アクセス時は本当にイニシャルなデータですが、2回目以降は前回のデータが復元されるというわけです。僕はこのステップを **Restore** と呼んでいます。

## 2. Redux --> Firebase

![qiita-firebase-3.png](https://qiita-image-store.s3.amazonaws.com/0/74793/66ba2762-7620-6da8-3d1f-8eb37c3f79bc.png)

**Restore** 後は基本的にReduxからFirebaseへの一方通行で変更がある度にデータを送り続けます。つまりバックアップし続けます。

「基本的に」と書いたのは、クライアントが1台だと想定した場合の話です。複数クライアントで同じページを開いているときは同期させることもできるので、その場合は適宜Firebaseからデータが降ってきます。

## デモ

デモは [こちら](https://ovrmrw.github.io//qiita-advent-calendar-2016-firebase-2/) です。複数のクライアント(ブラウザ)で開いて編集してみてください。リアルタイムに同期することがわかるかと思います。

## 別にReduxじゃなくても良い

ここまでReduxで話を進めてきましたが、要するにFirebaseにステートをバックアップできて後でそれを復元できるのであればStoreがReduxである必要はないですね。
僕も公式Reduxを使っているわけではなく、それっぽいものをRxJSで書いているだけなので。

ちなみに下記はReduxのドキュメントに書かれている有名な一節です。
> The question is: do you really need Redux if you already use Rx? Maybe not. It's not hard to re-implement Redux in Rx. Some say it's a two-liner using Rx .scan() method. It may very well be!

> 問題は、既にRxを使用している場合、Reduxが本当に必要かどうかです。 そうでないかもしれない。 RxでReduxを再実装するのは難しいことではありません。 Rx .scan（）メソッドを使用するのは2つのライナーだと言う人もいます。 それは非常にうまくいくかもしれません！

Google翻訳でトランスレートしましたが概ね正しく翻訳されています。

## JRFJ ... JRFJRFJRFJ ...

JSON → Redux → Firebase → JSON → Redux → Firebase → JSON .....

![qiita-firebase-1.png](https://qiita-image-store.s3.amazonaws.com/0/74793/58223cc2-dd3b-0ae6-b9b7-7311f9444300.png)

クラウドとクライアントの間をステートフルにする試みでした。

[Redux部分構築のソースコード](https://github.com/ovrmrw/qiita-advent-calendar-2016-firebase-2/tree/master/src/lib/store) はほぼ全編RxJSで書いています。

---

以上です。

ここまで読んでいただいてどうもありがとうございました。

明日は [@otakky](http://qiita.com/otakky) さんです。