isyumi_netブログ

isyumi_netがプログラミングのこととかを書くブログ

MySQLからリアクティブにFirebaseを更新するものを作った

コンセプトは f(RDB)=> Firebase

モチベーション

Firebase Realtime Databaseを使う上でこのような問題点がある

  • あえて非正規化した場所が、バグによって正しく更新されず不整合になるリスク
  • 正確なアクセス権限設定が極めて難しい
  • 枯れたRDBほどマイグレーションツールが揃っていないためAlter Table的な操作をしたくない

そこで、Realtime Databaseの前にRDBを置き、RDBからReactiveにRealtime Databaseを更新すれば解決できると考えた。

実装

MySQLから更新イベントを受信するためにはmysqlbinlogというコマンドを使う。
これは本来全然別のことに使うためのものだが、せっかくなので使う。

RDBの全データを引数にRealtime Database上の全データを作る純粋関数を書く。

mysqlbinlogが標準出力に何かを書いたら、それに反応してfirebaseを更新する。

サンプル

void main() async {
var command = "mysqlbinlog --read-from-remote-server --short-form --host=localhost --user=your_name --password=*** --stop-never mysql-bin-changelog.000001";

var process = await Process.start(command , []);
process.stdout.listen(onStdout);

}


class UserEntity {
int userID;
String name;
}

class MessageEntity {
int messageID;
DateTime time;
String text;
int userID;
}

void onStdout(List<int> data) async {
print("-" * 10);

var conn = await MySqlConnection.connect();

var result = await conn.executeStreamed("select * from users");

List<UserEntity> users = [];

await for (var row in result) {
var user = new UserEntity()
..userID = row[0]
..name = row[1];
users.add(user);
}

var result2 = await conn.executeStreamed("select * from messages");

List<MessageEntity> messages = [];

await for (var row in result2) {

var message = new MessageEntity()
..messageID = row[0]
..time = row[1] as DateTime
..text = new Utf8Decoder().convert((row[2] as Blob).toBytes())
..userID = row[3];
messages.add(message);
}

messages.sort((m1, m2) => m1.time.compareTo(m2.time));


var fbMessages = messages.map((m) => <String, dynamic>{
"time": m.time.toLocal().toIso8601String(),
"text": m.text,
"userName": users.firstWhere((u) => u.userID == m.userID).name,
}).toList();


await fbClient.put("/messages",fbMessages);

}

今後の課題

今はパフォーマンスが悪すぎるので改善したい。

  1. バイナリログをきちんと読み取って、毎回全データを落としてくるのではなく、関係のある行のみを落としてくるようにしたい
  2. Realtime Databaseに全データを書き込んでいるので、今回の出力と前回の出力を比較して差分更新できるようにしたい
  3. 今はRealtime Databaseに書き込む全てのデータを毎回作っているので、関数を小分けにして、無関係な部分は実行しないようにしたい

また、クライアント側からのアクションをMySQLに書き込むところも上手く抽象化したい

日本は今すぐUber Eatsのオープンプラットフォームを作るべき

Uber Eatsがよかった

今日、初めてUber Eatsを利用して見た。UXが素晴らしかった。

  • 店舗を選ぶ
  • メニューを選ぶ
  • 自分の住所を入れる
    • 料金+配送料が表示される
  • クレカを入力
  • OKボタンを押す
  • 配達員さんが今どこにいるのか地図に表示される

という完璧なUXだった。
Webアプリは簡潔でわかりやすかった。
すでにUber Eatsにハマりそうである。

Uber Eatsが解決した問題

そもそも、僕はあまり飲食店を開拓しない方だ。いつも同じ店に行っている。
新しい場所というだけで落ち着かない。
店ごとに注文方式、メニュー体系、前払いか後払いかが違う。

デリバリーも可能だが問題がある。多くの場合電話を強要される。電話が嫌なのだ。

ネットで注文できる店もある。
しかし、店ごとに

  • パスワードを作って会員登録
  • 住所入力
  • クレカ登録

をしなければいけない。
割りに合わない。
また、飲食店のホームページは店側の見せたい情報がてんこ盛りで、なかなか目当ての情報にたどり着けない。
自分に必要な情報が整然と並んでいて欲しいのだ。

しかし、Uber Eastsはすごい。

まず、今後ここに出店している店舗に注文する限り同じ会員情報を使いまわせる。
店舗数は結構多いし、ガストなどメニューが広い店舗もあるので、飽きることはないだろう。

Webアプリは、非常に整頓されていた。見苦しい宣伝文が少ないし、どんなメニューがあるのか一目瞭然だった。
料金体系も非常に的確なフィードバックがなされてわかりやすかった。

なぜ今までこれがなかったのか

僕はちょっとがっかりした。
Uber Eatsの要素を見れば、20年前の技術水準で十分に似たようなものは作れたはずである。
なぜ今まで日本の外食産業はこれを作らなかったのか。
今になって外資系に高額な手数料をふんだくられているのか。
日本のプラットフォーム構築能力の低さにがっかりする。

今からでも遅くない。すぐに取り掛かるべきだ。

するべきこと

前提として一社がプラットフォーマートして一人勝ちする構図は避けたい。
まず、各店舗がメニュー表を自分のドメインから所定のXML的なものでフィードする。
そのフィードをお客さんが自分のアプリで閲覧する。
フィードのフォーマットはオープンに定められ、誰が対応するアプリを作ってもいいことにする。
フィードは過剰な装飾ができないフォーマットになっているべきである。
お客さんは自分のアプリから注文する。
この時の注文プロトコルも事前に決めておく。
配送業者の位置情報を追跡するための鍵交換のプロトコルも必要であろう。

ブラウザの画面を転送するものを作った

ブラウザの画面をもう片方の画面に転送するものを作った。

 

動機

AWSがWebSocketのリバプロに対応してくれた。

これまでWebSocketでいろいろ作った。楽しかった。しかし、一つ問題があった。WebSocketを使うとインフラ・デプロイの自動化が不可能になる点だ。HTTPと違いユーザーが使っている間はずっと接続が維持される。つまり、ユーザーが一人でも残っていたらサーバーを捨てれないのだ。

そこで、Nginxなどを使って自前でWebSocketリバースプロキシを立てるのがこれまでの習慣だった。当然、そのサーバーにパッチを当てたりするのが嫌になる。

この度、それをAWS側が巻き取ってくれることになった。ありがたい。せっかくなので前々から作ろうと思っていたこのシステムを作った。

 

用途

ユーザーテストとかに使えると思う。

あと、無許可でお客さんに対してやるのはやめましょう。 

動作要件

送信画面は数行のJSを入れられればなんでもいい。

WebSocketサーバーは別のドメインになっても問題ない。

受信画面は送信画面と同じドメインに存在する義務がある。これは主に相対アドレス解決の問題である。無理なら手元にプロキシサーバーを立ててhosts.txtに入れればよいだろう。WordPressを使うなら、共通ヘッダーに受信用スクリプトを入れて、固定ページに送信用スクリプトを入れればいいだろう。

やっていること

  1. 画面の変更を検知
  2. innerHTMLを取得
  3. それをWebSocketで別のブラウザに転送
  4. innerHTMLをサニタイズ
  5. 表示

画面の変更検知

  1. DOMの変更検知
  2. Touchイベント
  3. InputEvent
  4. Scrollイベント
MutationObserver

 

developer.mozilla.org

DOMの変更を検知するもの。以前はDOMNodeInsertedなどを使っていたが、現代ではこのAPIを使うべきだ。このAPIはコールバック関数が非同期で起動されるのでユーザーにやさしい。

Touchイベント

Start/End/Moveイベントを拾うことでスワイプをたどることができる。

毎度毎度clientYとかscreenYとかpageXとか、どれを使えばいいんだっけとググる羽目になるので、正解を書いておく。clientXとclientYだ。

InputEvent

MutationObserverとinnerHTMLだけではinput要素の中を取れないようだ。どのハンドラを使うべきかだが、

  • onChangeだとフォーカスが外れるまで発火しない
  • onKeyPressだと最新のvalueが取れない

という点を考慮し、今のところonKeyUpイベントをハンドルするのが正解ではないかという気がする。

一つ問題がある。DOMの中の特定の要素を一意に指すことができるグッドな方法がないことだ。一般的にXPATHが使われる。しかし、JSにはあるDOMのXPATHを取り出すAPIXPATHからDOMを取り出すAPIもない。

そこでその要素は同じタグの何番目というXPATHもどきを作った。

String getPath(HtmlElement element) {
  var tagName = element.tagName.toLowerCase();
  var tags = document.querySelectorAll(tagName);
  var elementIndex = tags.indexOf(element);
  return "${tagName}/${elementIndex}";
}

これなら画面を超えて一意に特定のDOM要素を指すことができる。

Scrollイベント

当たり前の話だが、目上の人のwindowのscrollイベントをハンドリングするときはpassiveを指定するのがマナーです。

innerHTMLの送信

おそらく

  • HEADタグの中身
  • BODYタグの中身
  • 現在のスクロール位置
  • 現在のURL

を1セットで送るのが合理的なようだ。理由は↓。

サニタイズ

まず、innerHTMLをDOMに変換したい。いきなりwindow.documentに入れてしまうと大変なことになる。

DocumentFragmentというブラウザのAPIを使うのが正しいと思う。自分はDartで書いたのでJSの詳細なAPIは存じ上げないが、nsTreeSanitizerというのを正しく設定しないとダメなようだ。Dartの場合はtreeSanitizer: NodeTreeSanitizer.trustedでおk。

次にHEADのDocumentFragmentにbase要素を入れる。こうすることで送信画面と同じCSS・imgを見に行ってくれる。

次にスクリプトを削除する

  • スクリプトタグを全部消す
  • 各要素のonで始まるattributeを全部消す
  • Link rel="serviceworker"を前部消す

で大丈夫だと思う。違ったらだれか教えてください。

準備ができたらnodeを入れ替えてScroll位置を設定すれば大丈夫。

 

 

MSが社運をかけて世界を変える賭けに出たらいいな

文脈

最近のWeb界隈でよく話題に上るテーマがある。Webの後継技術についてだ。主に二つの理由による。

  1. 技術的に古い
  2. 仕様が肥大化し過ぎた

そこで、後方互換性を切りモダンな設計で新しいWebを作りなおすべきだという夢が語られるようになった。

次世代のWebとは

おそらく

あたりをモダンなものに置き換えれることになるだろう。それぞれ

  • HTML/CSS
  • HTTP
  • JS
  • jpg等

に対応する。

Googleの動向

Googleは次世代のWebを画策しつつ、現行のWebを少しずつ前進させていこうという二正面作戦を取っているように見える。比重は2:8くらいか。僕も今日まで次世代Webの旗手になるのはやはりGoogleかと思っていた。

今日起きたこと

MSがEdgeのレンダリングエンジンを独自のものからBlinkに切り替えるという発表があった。大変残念なことだ。MSが今後Webの世界で覇権を取ることが難しくなったとみられる。
断っておくが、別に僕はGoogleやMSが覇権を取って欲しいと思っている訳ではない。競争によって技術革新が起こって欲しいのだ。だから、MSというGoogleと対等に渡り合える数少ないプレーヤーがリングを降りたことを残念に思う。

そうであれば、MSはWebの後継技術を大々的に打ち出して競争をひっくり返して欲しい。
MSは莫大な資金力と世界最高峰のソフトウェア技術力と絶大なブランド力と強大なインフラとナイスガイなCEOを持つ最強の企業の一つだ。特にクラウドプラットフォーム事業が絶好調であることが好材料だ。OSやOfficeなど過去の資産で食っている状況ではなく、モダンITの世界で気を吐いている。今のGoogleに敵う可能性を持ったほぼ唯一の企業だ。
これからMSがGoogle

の牙城を崩す可能性は低い。
そこで、起死回生の一発として次世代Webの主導権を取りに来て欲しい。

スマホ用Webアプリづくりには限界がある。

スマホ用Webアプリづくりには限界がある。

 

この2ヶ月間、あえてiPhoneからTwitterアプリを消して、ChromeTweetをしていた。

Twitterスマホ向けWebアプリはネイティブアプリに比べてあまりにも操作感が悪かった。

Facebookも同様だった。

TwitterFacebookという、おそらく世界で最も金と時間と技術がかかっていそうなWebアプリでもこの程度の体験になってしまうのかとがっかりした。

 

そこで、不満点の指摘と提言をしたい。

題材としてTwitterを取り上げるが、決してTwitterの悪口ではない。

むしろ普段スマホ向けWebアプリを作ってる身からすると、この苦労は大変良くわかるし、Twitter Webアプリはすごく良くできていると思う。

だから「世界最高峰の人たちでもこうなるんだぞ、今のWebには色々足りないものがあるんだぞ」ということを言いたい。

 

不満点

Pull To Refreshの判定がきつい

Pull To Refresh(以下PtR)とは、画面を下向きに引っ張ることでコンテンツを更新できる機能である。

多くのアプリに採用されている。

Chromeもそのジェスチャでページのリロードができるようになっている。

Twitterネイティブアプリはそれでタイムラインを更新することができる。

Twitter Webアプリは画面の中央を下向きに引っ張ると同じことができる。

しかし、タブメニューのあたりを下に引っ張っるとTwitterがJSで実装したPtRではなくChrome側のPtR(以後ネイティブPtR)が反応してしまう。

中央を引っ張った場合でも、タイミングによってはネイティブPtRが反応してしまうことがある。

 

Context Menuが意図せず立ち上がる

コンテキストメニューとは、PCの場合右クリックした時に出るメニューだ。

ピーや印刷などの操作が選べる。

スマホのブラウザの場合、テキストを長押しするとそのメニューが出てくる。

これが厄介者である。時々そんなつもりがなくても立ち上がってしまうことがある。

iPhoneの場合、急に文字列選択UIが出てくる。

キャンセルしようとして適当な場所を押すといいねを押してしまったりする。

 

別の画面に行ってから戻ってくるとスクロール位置が保存されていない

この手順で再現する

  1.タイムラインで結構下の方までスクロールする

  2.適当なツイートをタップしてTweet詳細画面を開く

  3.戻る

  4.さっきと違い場所にいる

 

ポップアップを開いている状態で後ろの画面がスクロールしてしまう

Tweet右上のドロップダウンメニューを開いた状態で上下にスワイプすると後ろで本体がスクロールされている。

ちなみに、多くのWeb開発者は、現在のスクロール位置を変数に保存した上で後ろの画面をposition:fixedにするということをしていると思う。

 

画像のトリミングができない

意外なことにTwitter のWebアプリではトリミングができない。

これはわからなくもない。

自分もWebアプリでトリミング機能を作ったが、必ずちょっと残念な感じになる。

まず、結構カクつく。また、左から右にスワイプすると、ブラウザの戻るジェスチャにイベントを取られてしまうことがある。ピンチイン・ピンチアウトも指の置き場が悪いと画面全体が縮小・拡大してしまう。

 

なんかウニャウニャ動く

ネイティブアプリの場合、アプリの外枠が決まっており、それが動くことはない。スワイプで物の大きさが勝手に変わったりしない。

しかし、Webアプリの場合、アプリの外枠や各要素が不自然にリサイズされたりオーバースクロールしたりする。

 

良かった点

悪口ばかり言っては申し訳ないので補足する。

無限スクロール

無限スクロールとは下にスクロールすると、どんどん新しいコンテンツをサーバーから取ってきて下に追加していってくれる機能だ。

普通にスマホWebアプリで無限スクロールを実装するとかなり怪しい挙動をする。

これだけスムーズに無限スクロールが動いているということは、中の人が結構工夫したと思われる。

ちなみに僕はIntersection Observerとそのポリフィルをつかった。多分パフォーマンス悪いと思う。


提言

前提になる問題として、スマホのブラウザはバテリー節約のためOnTouchMoveEventとOnScrollEventの発生頻度を抑えている。

また、スマホブラウザがネイティブで持っているジェスチャUIは、多くのWebページ閲覧に必要なものだと思うので無くせとは言えない。

なので、スクロールやスワイプのイベントを取らなければ実現できないようなことは、だいたいHTML標準に入れておいてほしい。

HTMLにPtR要素を追加してほしい

また、Document内にPtR要素がある場合はネイティブPtRをオフにしてほしい。

無限スクロール要素もHTMLに追加してほしい

同上

画像トリミング画面を呼び出せるAPIを作って欲しい。あるいは矩形選択要素のようなものをHTMLに追加してほしい。

同上

レイヤーの概念とSegue的な概念を入れてほしい

今の所多くの開発者はBodyの中に適当なDivを作り、スタイル属性のpositionやz-indexを使って階層を表現している。

あるいは諦めて別Tabを開いたりする。

しかし、思い切ってサブBodyのようなものを作れるようにしてはどうか(というかDialog要素はどこへ行った)

その要素へのイベントは一切上要素に伝播されないようにしてほしい。

その要素はHistoryAPIとうまい感じに連携して戻る・進む・リロードの動作が違和感ないようにしてほしい。

スクロール位置はサブBodyごとに持って欲しい。

ちなみに、cssのoverflow:scrollを使えばいいのでは? と思った人がいるかもしれないが、最大スクロール位置を超えてスクロールすると微妙な感じになるのでちょっとストレス。

ContextMenuをオフにすることをベストプラクティスにしてほしい

これは既存のJSでできる。しかし、嫌悪感がある人も多いだろう。その人は多分おっさんである。

2000年代、まだWebページのプラクティスが確立してなかった頃、JSでマウスイベントやスクロールイベントを謎フックをしているサイトが跋扈し大変うざがられていた。

マウスにカモメがついて着たり、右クリックをすると画面がチカチカしだすなどの演出を盛り込んでる人がいたのだ。

そのため、「極力ブラウザの標準UIを妨げない」というのがお約束になった。

しかし、スマホにおいてTwitterのようなWebアプリでコンテキストメニューを出せるメリットはないだろう。

ここは思い切って「文書度合いが低くアプリ度合いが高いスマホ向けWebページはコンテキストメニューをオフにするべき」ということをベストプラクティスとしていくべきではなかろうか。



根本的な問題

WebからスマホのネイティブUIコンポーネントを作れるようにしてほしい。



Rust / Wasm でオセロ盤を作った。

Othello

 

オセロAIはまだ作れていない。

ただの石が自動でひっくり返る板。

 

Rustの使い心地

C++を書いている時に脳内で考えていたことが簡単な記号で言語化できるから嬉しい

GCのある言語しか使ったことない人にはちょっと敷居が高いかも

 

楽しかったところ

オセロの盤面を配列ではなくてintで表すようにしたこと。

オセロの盤面は64ますで、空・白・黒の3通りだから全部で 3の64乗通りである。これはRustのu128で表現可能である。

僕はu128の上に3進数を構築した。

これでスタックとかヒープとか気にしなくてよくなる。

 

ありがたかったこと

wasm-bindgenは最高である。

buildすると自動でd.tsを吐き出してくれるため、TypeScriptからasync import一発でライブラリを読み込める。型補完もバッチリ。

また、生WasmだとちょっとめんどくさいはずのArrayBufferのやりとりも抽象化されている。多分、wasm-bindgenなしなら自力でLenとOffsetをとってポインタを渡してあれこれしないといけないはずだ。

しかし、この通りの簡単な関数呼び出しで配列をやりとりできる。

github.com

新しいWebフレームワークを作る。

話の前提

ここだけの定義

  • クライアントとはWeb/Android/iOS/CLI/管理画面を指す
  • この話の中でGAEのDataStoreとMemcacheを同一のものとみなす。なぜなら多くの場合その2つを透過的に扱うライブラリを使うから
  • ローカルキャッシュを全てIndexedDBとひとくくりにする。AndroidSQLiteなどは適宜読み替えてほしい。また、サーバーから取ってきたデータは全て一度IndexedDBに入れることを想定している。
  • とりあえずReduxの話をする。適宜読み替えてほしい。
  • RESTFul APIの全体をGETした時の戻り値を、とりあえずRESTFul API全体と呼ぶ。
  • DBは今の所特定の実装の話をしていない。ただ、GAEのDataStoreを意識している。
  • ReduxのStateに入れるデータにはサーバー起源データとユーザー起原データがあると思う。掲示板アプリなら「ユーザー一覧」「書き込み一覧」などがサーバー起源データだ。「キーボードから入力中のテキスト」「現在開いている板」などがユーザー起源データだ。

作ろうと思っているフレームワーク

作ろうと思っているフレームワークはこういうものだ。


ユーザーがすること

  • DBの全データをRESTFul APIの全体に変換する関数を書く
  • RESTFul APIの全体をIndexedDBの全データに変換する関数を書く
  • IndexedDBの全データをReduxのStateに変換する関数を書く
  • 通常のmapStateToProps関数を書く
  • RESTFul APIのPUT・PATCH・DELETE・POSTがDBのどの部分を更新するのか関数で定義する

フレームワークがすること

  • ビルド時に上記のコードを読みキワッキワに最適化した処理を自動生成する

思い立った背景


今、サーバーサイドをGAE/Go + Gin、フロントエンドをReactで開発している。
現代的なプロダクトは、サーバーとクライアントの双方が大きなモデル層を持つようになった。どちらかがもう一方に依存しなくなったということだ。昔のフロントエンドはサーバーサイドプログラムの携帯ストラップのような扱いだったが、スマホの時代になったら変わった。今ではサーバーとクライアントは完全に粗結合になっている。
また、GinとReactの共通点として、両方とも薄いライブラリであることを強調している。流れの早い業界の中で、「その気になればいつでも捨てられる」ということがアピールポイントになっている。これは現代的なフレームワーク全体に共通していると思う。
このように「各レイヤーが自立して動くようになったことで粗結合になったこと」「薄いライブラリが流行っていること」がプロダクトの健全性に大きく貢献している。しかし、その分、コード量という観点ではプログラマの負担が増えている。トラック一台で運びきれないのでワンボックスを30台用意しましたと言われた気分だ。
今こそ、がっぷり四つの分厚いフレームワークを作るときだ。自分ならできるという自信がある。
自分はずっとサーバーとクライアントの両方を担当してきた。また、サーバーとクライアントの接合部も設計してきた。一人で両方を担当しているが、しっかりと粗結合にしてきた。だから、どのようにサーバーとクライアントを接合するべきかわかっているつもりである。
悪魔のように粗結合に。天使のように密結合に。

根底にある思想


サーバと全てのクライアントはRESTFul APIの型のみに依存するべきだ。その向こう側にどんな処理があるか一切知っていてはいけない。
RDB用語で「関数従属性」という言葉がある。例えばA+B=Cは3つの内2つ値が定まればもう1個の値も定まる。このように全体の内一部がわかればほかも割り出せることを関数従属性があるという。ソースコードにも当てはまる。フロントエンドにこういうコードがあるからサーバーサイドにこういうコードがあるはずだと特定できることがある。これはソースコード間に関数従属性があると言えると思う。その場合、プログラマがコードを書くよりコンピューターにコードを生成させたほうが安全なはずだ。

支柱になる考え


DBの全データからRESTFul API全体に変換する関数のASTを取れば、RESTFul APIの一部をGETするリクエストを処理するためにDBのどのデータを取り出さなければいけないか算出できるはずだ。
ReduxのStateのサーバー起源の部分とIndexedDBは虫食いコピーの関係になるということにしておくといいと思う。IndexedDBに保存されているデータの内必要なものだけを随時Reduxにコピーして使う。
RESTFul API全体とIndexedDB全体も虫食いコピーの関係になる。
キャッシュ戦略はコンピューターが立てたほうが精度が高いはずだ。最も素朴な実装をプログラマが書き、それをもとにコンピューターにキャッシュ戦略を立案・実行させるといいと思う。
コードの自動生成という言葉を使っているが、

 を現時点では区別していない。一番合目的なものを適宜選ぶ。ただし、IDE補完との相性を重視する。また、ソースコードを自動生成する場合は生成の冪等性を重視する。つまり、一度生成したソースコードを人間が修正したら不磨の大典と化すことは避ける。

 

避けること

これはプログラミングが苦手な人のための簡単なフレームワークではない。自力で正確に型定義し、仕様をシンプルな純粋関数で表現でき、ちゃんとテストコードが書ける人しか使えないと思う。
jQueryWordPressプラグインにありがちな「環境上のどの変数を参照しているのかわからない、どんな副作用をもたらすのかわからない、でもインストールすれば勝手にいい感じにやってくれる」ようなものではない
少なくとも、

  • Cookie
  • DBのSchema
  • RESTFul APIの型
  • 公開するRESTFul のPOST・PUT・DELETE・PATCH
  • ReduxのStateの型
  • ReactのPropsの型
  • IndexedDBの型

は各プログラマが責任を持って完全に支配するべきだと考える。

このフレームワークは何をしてくれるのか


ユーザーがどこかの画面に来たときに、その画面を表示するのに必要なデータをRESTFul API、IndexedDBから自動で取り出してReduxのStateに読み出しておいてくれる処理を生成する。すると、プログラマはStateに必要な全てのデータが入っている前提で処理を書くことが可能になる。
また、各GET APIも自動生成してくれる。DBの全データをAPIの全データに変換する関数があるからプログラマの意図した通りに生成されるはずである。
次に、キャッシュレイヤーを自動生成する。静的ファイルを使うもの、DBに非正規化したデータを保存しておくもの、クライアントのファイルにキャッシュさせておくものなどがあると思う。
さらに、DBの一部分を更新した時に、クライアントのIndexedDBをPushで変えたい。それも自動化できる。