差分をめぐる旅 (2) - Virtual DOM から学ぶ

Posted on October 7, 2019

前回の記事から二年近く期間があいてしまいました。その理由として、

などが考えられるのですが、最初の二つは明らかに不要なこだわりでした。これからは気の向くままに書くことにします。さて、今回のテーマは「Virtual DOM から学ぶ」です。

周知の通り Virtual DOM はフロントエンド技術に多大な影響を及ぼしました。 Virtual DOM の基本的なアイデアは、今から見れば思いつかなかったのが不思議なほど単純なものでした。そのアイデアをすこし抽象化して表現すると次のようになります。つまり、スナップショット同士の差分を計算・利用することで特定のコストを削減できる、というものです。このように見たとき、実はこのアイデアは特に新しいものではないことがわかります。たとえば rsync は同じような発想のもとで作られています。 Virtual DOM が VDOM ツリー同士の差分を計算・利用することで DOM の更新コストを削減するのと同じように、 rsync はファイル同士の差分を計算・利用することでネットワークの転送コストを削減します。両者は分野も目的もまったく異なるものですが、動作原理はだいたい同じです。注意点として、総合してプラスにするためには、スナップショットおよび差分の計算が、削減したいコストに比べて安く済む必要があります。 Virtual DOM は、かなり気づきにくいですが、この条件が成り立つのです。

rsync を知りながらも、フロントエンド技術とはまったく無関係なものとして扱っていたことに、当時の私は衝撃を受けました。同じような過ちを犯さないようにすることは不可能ですし、そうすべきでもないのでしょう。しかし、学ぶべき点が二つあります。一つは、上記したアイデアは想像以上に普遍的で強力である、という点です。もう一つは、さらに重要な点ですが、差分更新として機械的に導出できる処理を極力手書きしてはならない、という点です。この点をもう少し深堀りしてみましょう。

簡単な Web アプリケーションを考えてみてください。バックエンドでのデータベース更新処理は現在は当たり前のように手書きされます。しかし、実はこれは差分更新として機械的に導出できる処理の典型例です。データベースのスナップショット(あるいは状態)を State、ユーザーが発行した操作を Action とすれば、データベースの更新は、現在の状態とアクションを取って次の状態を返す (State, Action) -> State という関数で記述できます。次の状態さえ計算できれば、新しい状態と古い状態の差分を計算し、その差分をもとにデータベースに INSERTUPDATE を発行すれば差分更新は完了します。かなり理想化・単純化していますが、原理的には差分更新を手書きする必要がないことがわかると思います。このように考えることに抵抗がないと言えばうそになります。特に普段から現実的な問題に直面している人にとっては机上の空論のように見えるかもしれません。しかし、新たな Virtual DOM を見逃さないためにも、このような考え方に意識的に慣れておく必要があります。

ちなみに、(State, Action) -> State という関数でのデータベース更新の定式化は、 Event Sourcing とまったく同じか、あるいはすくなくとも似た考え方です。まったく別の領域ではたとえば、 Redux や Elm がモデルの更新について基本的に同じ定式化をしています。基本的な発想は良さそうですが、腑に落ちない部分があります。それは Action とは何なのか、という点です。個人的には、 Action に何らかの性質を要求したいし、そうすべきだと考えています。具体的には Action は差分であるべきだろうと思うのですが、そうするといくつか問題がでてきます。(あるいは顕在化します)このあたりの理解はまだすごくぼんやりしているので、なにかわかったらそのときに書きます。

話を戻します。RDBMS と正規化は切っても切れない関係にあります。正規化が重要視される背景には、差分更新を手書きせざるをえない現実的な制約があります。もしそのような制約を取っ払えれば、つまり、差分更新を手書きせずに済むのなら、実は正規形はどうでもいいということになります。どうでもいいというのは語弊があるかもしれません。現実的には、正規形が複数を存在してもよい、ぐらいに落ち着きそうです。とにかく、今ほど正規化が重要視されなくなるでしょう。個人的に、これは大きな発想の転換のように思います。正規形が実際には複数存在してもよいと考えられるのは、何も RDBMS を利用するソフトウェアに限った話ではありません。むしろそれは氷山の一角だと考えています。

あらゆるソフトウェアがたった一つの正規形あるいは基底となるデータ設計を暗黙のうちに想定し活用しています。これは普段まったく意識されません。どのようなデータ設計を採用するかは現在はソフトウェアエンジニアの腕にかかっています。腕の良いエンジニアなら、ソフトウェアの要件にぴたりとあったデータ設計、具体的にはアクセスが効率的で不整合の発生しにくいデータ設計をうまく考え出します。我々はこの職人技を少しでも機械的な作業にしたいと考えています。詳しく言えば、データ設計のうち本質的な部分と非本質的な部分を分離して、非本質的な部分は機械的に処理したいと考えています。今回のテーマである Virtual DOM は、データ設計を DOM の都合から分離した点でも大きな進歩だったと言えるでしょう。とはいえ、 Virtual DOM 登場後も、いわゆる「モデル」のデータ設計がたった一つの正規形にこだわっていることに変わりはありません。そして、不必要にたった一つの正規形にこだわっている例は、フロントエンドの領域の外にも、まだまだ残っていると考えています。

次回は差分の代数的構造について書きたいと考えています。

以降は補足です。今回はトップダウンに差分を計算する方式についてのみ触れました。差分を計算する方式には大きく分けて、スナップショットからトップダウンに差分を計算する方式と、ベースとなる差分からボトムアップに次々に差分を導出していく方式の二つがあります。ボトムアップ方式の差分計算は、トップダウン方式に比べて難しいので、あまり注目されません。しかし、たとえば上記したデータベースの更新の定式化を現実的なものとするためには、どうしても欠かせないものになります。トップダウン方式の差分計算では、たとえば 1000 万件あるテーブルをちょっとした更新のたびに作り直す必要があり、これはどう考えても現実的ではありません。そういうわけで我々はボトムアップ方式の差分計算をある意味本命だと考えています。これについてはまた機会があれば書きたいと思います。