2013年9月18日水曜日

関数プログラミングのボトルネックとしてのRDBMS

プログラム開発は、多くの人々が目的達成のため、もがき苦闘するタールの沼である

– Frederic P. Brroks, Jr., 人月の神話

モジュール性はプログラミング成功の鍵である

– John Hughes, 関数プログラミングはなぜ重要か


タールの沼の底から

 タールの沼と聞いて連想するのは大規模なSIである。業務アプリケーションやWebアプリケーションは規模が大きくなればなるほど、複雑さが増し収拾がつかなくっていく。そしてそのようなアプリケーションを大きな単位で上手くモジュール化し、さらには再利用することは不可能に近い。
その技術的な原因の一端、そして問題を解く鍵は、そのようなアプリケーションが常に携えているRDBMSの周辺にあり、さらに言えばおそらくRDBMSとアプリケーションロジックの組み合わせにあると考えている。
ここでは、アプリケーションロジックの実装に関数プログラミングを適用してもこの問題が容易には解決しないことを示し、この問題の根の深さと重要性を主張したい。

関数プログラミングの強み

関数プログラミングは、非常に強力である。その所以は関数が提供する高いモジュール性にある。
関数の純粋性を損ない、モジュール性を阻害する副作用はモナドを使って記述し、安全に分離しておくことができる。
これが関数プログラミングの強みである。

関数プログラミングの弱み

では関数プログラミングの弱点はなにか。
それは副作用を含む処理が大半を占めるようなシステムのプログラミングに対しては、その強みが発揮しづらいことである。そのような副作用が支配的なプログラミングとして、UI、ネットワーク、データベースプログラミングなどがある。どれも重要だが今回のターゲットはデータベースプログラミングである。

UIプログラミング

UIは、そもそもインタラクティブな入出力を扱うものである。ウィジェットやフォームといった部品がそれぞれ状態を持ち、ユーザからの入力という副作用を元にそれらの状態・表示を更新していかなければいけない。このため多くのロジックは必然的に副作用を含むものとなる。
このUIプログラミングはリアクティブプログラミングの主な応用領域のひとつである。UIとリアクティブプログラミングの組み合わせ、UIとRDBMSの組み合わせに関しての詳しい議論は別の機会としたい。ここではリアクティブプログラミングにより一応の解決策が与えられているとしよう。ネットワークプログラミングについても、リアクティブプログラミングの応用例があり、UIプログラミングと同様にここでは議論しない。

データベースプログラミング

最後がデータベースプログラミングでありこれが今回のターゲットである。
RDBMSとのやりとりもまた副作用であり、UIがシステムの末端に位置するのに対しRDBMSは中心に位置することが多い。
このRDBMSに対する副作用が、いかにモジュール性を阻害するかについて考えてみよう。

ボトルネックとしてのRDBMS


RDBMSを中心とするシステム、特にWebアプリケーションや業務アプリケーションなど、
外部からの入力をもとにRDBMSを操作するシステムにおいては、
関数プログラミングの利点は大きく阻害されることになる。
その理由について、リッチクライアントに対してAPIを提供するWebアプリケーションサーバの実装を例に考えてみたい。

API

APIはクライアントからのリクエストを入力に取り、なんらかのレスポンスを出力する。
ここでレスポンスが、リクエストの内容だけから一意に決まるのであれば、レスポンスを生成するロジックは純粋な関数として記述でき全く問題ない。
しかし、そのようなAPIは明らかに非常に稀であり、多くのAPIがリクエストの内容に基づきRDBMSに対するクエリーを発行し、その結果をもってしてレスポンスを生成することとなる。
このようなAPIは副作用を含む手続きであり、純粋な関数としてみなすことはできない。
APIの内部のドメインロジックを分解していっても、重要なロジックほど内部にデータベースとのやりとりを含む。

データベースのモジュール化の難しさ

そこで状態の側、データベース自体を分割しモジュール化すればよいように思える。各テーブルやそれをまとめるスキーマといったものを利用できないだろうか。テーブルをグループ化することはもちろん出来る。しかしアプリケーションロジックをそのグループに合わせてモジュール化しようとしても、なかなか上手く行かない。なぜなら、個々ののAPIやビジネスロジックが、苦労して作ったテーブルのグループ分けをまたぐようにして状態にアクセスすることが頻繁に発生するからだ。
個々のロジックが複数のテーブルを結びつけていき、綺麗に分けることのできない塊にしてしまうのだ。

巨大な単一オブジェクト

その結果として、この種のプログラミングは、
RDBMSという複雑な状態を内部に抱え、メソッドとして多くのAPIを持つ、
巨大な単一オブジェクトの実装という様相を呈することになる。
俯瞰してみた場合のプログラムの大まかな構造は関数プログラミングの技法を使おうが使うまいが、変わらないということになってしまう。

オブジェクト指向とMVC

この問題は、オブジェクト指向やMVCといった技法に基づきデータベースを抽象化しても全くかわらない。
またオブジェクト指向に基づきオブジェクトに分解することはもちろん可能である。しかしカプセル化や疎結合を実現しようと努力しても状態を通じた暗黙的なオブジェクト同士の依存関係を断ち切ることはできない難しい。可変状態を抱えるオブジェクトを苦労して分解したところで、それをつなぐ手続きの記述が煩雑になるばかりで全体の複雑さはなかなか下がらない。それはSOAの苦戦が示すところである。
オブジェクト指向の効能はもちろんある。しかしここで重要なのは、俯瞰的に見れば 手続き+RDBMS という構図は変わらないということだ。そしてそもそも手続きではなぜ駄目なのか、という点についてきちんと議論するのは本当に難しい。それを示すためには、手続き以外のもの、関数プログラミングやリレーショナルモデルが何を提供しているかについて詳しく見ていく必要があるだろう。オブジェクト指向に関しては今回の主要なターゲットではないので、別の機会に詳しく議論したい。

ではリレーショナル・データベースが「悪い」のか

そもそもRDBMSではなく、ほかのデータモデルに基づくDBMSを使ったらどうだろうか。しかし、ドキュメント指向データベースやグラフデータベース、オブジェクトデータベースなどを用いても問題が解決することはないだろう。
もしくは、そもそもDBMSを使わないという選択肢はないだろうか。
DBMSを使うその理由は永続化やトランザクションのためだけだろうか。
もしメモリ空間が無限大で、プロセスが永遠に落ちないのであれば、
データベースなど必要ないのだろうか。

おそらくRDBMSを使わないという選択は、問題を悪化させる。それは、RDBMSの提供するトランザクションとリレーショナルモデルがすでにかなりの程度問題の度合いを軽減してくれているからだ。
この点については他の機会に詳しく議論したい。

気がつけばそこはタールの沼の底だった


各々のAPIは、DBMSを通じて暗黙的に相互に依存し合い、それにより個々のテーブルも結び付けられていき、さらに各「画面」もまた複数のAPIにまたがることになる。斯くしてAPIや画面といった単位より大きなモジュール性は完全に阻害されることになる。

ではどうすればよいのか

結局のところ問題の核心はどこなのだろうか。
RDBMSだけ、もしくはアプリケーションロジックだけであればそもそも問題は起こらなかったはずだ。その2つを組み合わせたときに初めて問題が起こるのである。
そしてこれは古の問題、パラダイムやモデルの異種接続が引き起こすインピーダンスミスマッチングよりも、おそらく根が深い。
RDBMS内の状態とアプリケーションロジックの結びつきがモジュール性を阻害することが問題の原因であるならば、それらの境界だけを見るのではなく、システム全体に視野を広げ、データベースモデルとプログラミングパラダイムの両面から抽象化のやり方について考え直す必要があるのではないだろうか。
さらにDBMSと汎用プログラミング言語という分業体制を懐疑的に眺めつつ、もし全体の抽象化についてやり直すとしたらどうすればよいか、ということもおそらく考えてみるべきだろう。

次回の予定

リアクティブプログラミングをもってしてもこの問題を解決するのは容易ではない。
それは第一にUIプログラミングと違い、状態がメモリ内部ではなく、DBMSという外部にあるからであり、第二に、データベースを宣言的な形で隠蔽・抽象化するということは、別のデータベースモデルを作ることに限りなく近いからだ。UIとリレーショナルモデルとリアクティブプログラミングについて考える前の準備として、次回はリレーショナルモデルの恩恵と問題点について考えてみたい。

ご意見募集

実務家、理論家の双方の方々からのご意見ツッコミを切に望みます。
コメント欄もしくはtwitterの@pokarimへどうぞ。

ログ

 2013/9/19:「オブジェクト指向とMVC」に修正・追記した。
 2013/9/19:「ではどうすればよいか」に追記した。

0 件のコメント:

コメントを投稿