要するに
f(RDB , ユーザーイベント) = UI
序文
僕がWebアプリケーション開発を始めてから6年くらいずっと思っていたことです。
いわゆるIsomorphicの話ではないです。サーバーもフロントエンドも同じ言語で書くと楽だよねというようなことではないです。
本質的な主張
プログラマがアセンブラを書く時はレジスタの使い方を指示します。C言語を書くときはコンパイラが自動的に割り当ててくれます。一般的に、普通のプログラマーのスキルと比べれば、コンパイラに任せたほうがパフォーマンスが高いとされています。
同じように、サーバーサードプログラムとフロントエンドプログラムを分けず一つの言語で一つのアプリケーションを書き、機械的にサーバーとフロントの処理の分担を導出したほうが良いのではないかという主張です。
凄く極端な例
フロントエンドのソースコードの中に直接SQLが書かれていたらビビります。普通に考えてとんでもない脆弱性です。しかし、もしそのSQLが動的に実行されるのではなく、webpackの中でprepared statement化されて、実際のRPCはSQL通し番号と引数をやり取りしているだけであれば脆弱性ではないです。
このように、フロントエンドのコードを正にしてサーバーサイドの処理を生成することができます。
突き詰めれば、「最終的に画面に何を表示したいのか」を厳密に宣言出来るなら書式は何でもよく、そこから実装を導出できるはずです。
職業柄思うこと
そんなの無理だろor無意味だろと思うかもしれませんが、僕はこれを毎日頭の中でやっていて、大変つらいと思っているので、なんとか自動化してほしいというお気持ちはご理解いただきたいです。
自分はいつもDB〜サーバーサイド〜フロントエンドを一人で設計・開発・運用していることが多いです。
DB〜サーバーの間にキャッシュ用のKVSが挟まることが多いです。
また、サーバー〜フロントエンドの間にFirebaseを挟むことも多いです。
最初に画面に何を表示したいか考えます。次に、DBにどうやってデータが入っているか考えます。そこから、その間をつなぐ実装を逆算していきます。
考えることは
- この画面に必要十分なデータはRDBのどの範囲か
- ≒サーバー→フロントエンドの通信を最小化するには
- IndexedDBに入れて使いまわすか
- この画面を表示する度にRDBにクエリを発行して大丈夫か
- ダメならどんなキャッシュを用意しなければいけないのか
- そのキャッシュを更新する条件は何か
- WebSocketなどを使って更新を通知する必要があるか
などです。
ここで僕は2つのことを思います。一つは、計画立案自体を自動化出来るのではないかということです。もう一つは、この計画を元にソースコードを生成できるはずではないかということです。
上の「考えていること」は関数の最適化と言えます。
関数の最適化とはこういうことを言っています。まず最も素朴な関数を書きます。この関数はパフォーマンスがとても悪いです。しかし簡単に書けます。この関数を書き損じることはほぼないと考えます。その関数の挙動を変えないように式変形していきます。それによってパフォーマンスを向上させていきます。これを関数の最適化と呼ぶことにします。
ここでは、まず画面をRDB全体を引数に画面を返す関数を想像し、それを通信量とクエリ効率の観点から脳内変形してパフォーマンスを向上させています。
僕は、こういうことこそコンピュータに任せるべきだと考えています。自動化により計画立案ミスを防ぐこともがきます。また、人間は複雑な最適化を嫌がりますが、コンピュータなら嫌がらずにパフォーマンスのいいキャッシュ戦略を立案してくれると思います。実際、僕もキャッシュの種類を増やすことをとても嫌がります。管理が煩雑になり、その後の開発効率を落とすからです。しかし、コンピュータに立案させておけば、ビジネスロジックを変更する度に再立案させればいいので、気が楽です。また、自分のプロダクトでは効率のいい差分更新ではなく効率の悪い洗い替えをしているところがたくさんあります。めんどくさいからです。コンピュータならちゃんとやってくれると思います。
次に、コードの自動生成です。
自分はサーバーサイドもフロントエンドも書き慣れているので、上記の計画が立ったらその後に書くコードを隅々まで想像できます。僕に出来るんだから他の人にはもっといいコードが想像できるはずです。そうであれば、その計画→コードの変換を定式化しておくことで、コードの自動生成ができると思います。
統一言語はどんなものか
まず、SQLでDDLを書くということは無くなりません。
また、統一言語はVDOM的なテンプレートエンジンを持っています。そのテンプレートを書いた時点であるページの型が定まります。
統一言語はSQLのテーブルの型とページの型の2つを読み込みます。
そして、画面ごとにDB上の全データを該当ページのVDOMに変換する純粋関数を書きます。どんなにパフォーマンスが悪くてもいいので最も簡単でわかりやすい関数を書きます。
この言語で書いたWebアプリケーション定義をコンパイルすると、フロントエンドで実行されるJSとサーバーサイドで実行されるバイナリが出力されます。そのコードは、サーバー↔フロントエンドの通信の最適化やKVSを使ったクエリ結果のキャッシュなどが合理的に実装されています。
ユーザーイベントの扱いも重要です。
ユーザーイベントもReact的なものと同じ方針で扱います。
ユーザーイベントを値としてStoreに保存しておき、その値も上の関数の引数になります。実行時にその処理をサーバーサイドでやるのか、フロントエンドでやるのか、楽観的UIは必要か、などはコンパイラが考えてくれます。
よく表を絞り込みできるUIがあります。絞り込み条件をサーバーに送ってサーバーが絞り込みをしたほうがいいのか、該当しそうなデータを全部フロントエンドに送ってフロントエンドで絞り込みをしたほうがいいのか、悩むことが多いと思います。総合的にはサーバーサイドでやったほうがいいが、フロントエンドでやったほうがめんどくさくないからです。
でも、この仕組みなら、どちらを選んでもほぼ同じコストで実現できるので、常に合理的な方を選ぶことができます。
アクセス権限はどうするの?
あんまり考えていません。確かに、どこまでの処理をサーバー側でやってどこからフロントエンドでやるかをコンピュータに決めさせているので、予想外に秘密の情報をフロントエンドに送ってしまう可能性はあります。適当にアノテーションを入れたりすると思います。