大事なのは処理の分割でなく責務の分割

(前回の続き)HTMLテンプレート上に問い合わせ条件を書くっていうと、JSP上にSQLJDBC呼び出しをハードコーディングする暗黒時代の再来だと思う人も居るかもしれない。でも自分はそうではないと言いたい。それは責務が分割されているからだ。


URLを入力・またはフォームを送信し、データベースの内容に基づいてページを表示する、という処理を考えた時、実はいろいろな要素が組み合わさって実行されているとみる事が出来る。

  • 実DBのテーブルやカラムとの物理的なマッピング
  • エンティティ間のリレーション
  • 論理削除(deleted_dateが非nullのデータは削除と見なす、など)などのデータの取り扱い上の約束事
  • 状態フラグやワークフローの扱い(ブログサイトで、未承認のコメントは表示しないなど)
  • データの所有関係や権限の扱い(コメントは書いた本人のみ削除できるなど)
  • サービス(リソース)ごとのデータの切り出し方(掲示板のトピックをジャンル毎に表示するなど)
  • どのサービス(リソース)をどのページに表示するか(マイページorコメント一覧ページに新着コメント一覧を出すなど)
  • ページ内にデータ上のどの項目を表示するか(関連先も含めて)

レイヤーで言えば、この例だと、だいたい上に行くほどデータアクセスレイヤー、下に行くほどプレゼンテーションレイヤーに近いと言えると思う。


これらを個々のモジュールが担うべき責務だと考えると、例えばDAOにSQLを書き他のレイヤーからはそれを呼び出すだけというような、データアクセスレイヤーにぎゅっと集めてしまうような設計だと、責務が上手く分散できていないということになる。その結果、1項目の変更に対してもレイヤーを跨いで修正が必要になる。


権限の変更と表示内容の変更など、複数の要素に対して変更がある場合は仕方ないとして、表示内容や検索項目のような単一の要素の変更に対して、レイヤーを上がったり下がったりしないといけないとすると、作業効率がものすごく落ちる。編集対象があちこち別々の場所にあるし、意識のフォーカスの切り替えも必要だ。


CayenneのExpressionのように、問い合わせ条件をプログラム的に扱える事のメリットは、問い合わせ処理にまつわる責務を複数のモジュールに分割できることだ。今回のコードではアクションで直接SelectQuery#performQuery()しているけど、レイヤー間でSelectQueryやExpressionを持ち回せば、それぞれのレイヤーで条件を追加したり、変形したり、キャッシュを利用したりできるようになる。(例えばログイン情報を元に見せる情報を絞り込む、など)


データベースへのアクセスという処理は最終的に一カ所でおこなうが、それぞれの責務はモジュール毎に分割できるようになる。


責務毎にモジュールが分かれている事で、実装の分担も行いやすくなる。場合によっては部分的に、開発者ではなく、運用担当者に入力してもらう事が出来る場合もある。

SQLは悪か

上で「SQLをハードコーディングする暗黒時代」と書いたけど、SQL自体が悪とは思っていない。長年使われてきた実用に耐える構造化された問い合わせ言語であり、ビジネスルールを簡潔に表現する為の手段としてとても優れていると思う。


問題なのは、書かれたSQLをそのままDBに投げるしかないアーキティクチャだと思う。HTMLテンプレートに直にSQLを書いていても、それをパース・変形・アンパースできるとすれば、接続先やテーブルやカラムへの依存も発生しない。


実際問題、データベース依存のSQLを綺麗にパースして再利用するのは難しいかもしれない。そういう意味ではJPQLみたいな仕様化されたものは良いかもしれない。


我々オブジェクト指向プログラマSQLを軽視しがちだけど、それはSQLを「RDBMSという周辺機器を制御する為のプリミティブな制御コード」だと思ってるせいではないかと思う*1。 そうではなくて、ビジネスルールを簡潔に表現するためのDSLと考えれば、上手く適切に使えると思う。

SQLは正義か

SQLを動的に生成するようにすると、チューニング上の問題が出るという人も居る。自分としてはそれは、短期的には真だが、長期的には間違っている、と思う。


確かに、前回の例のように後から検索条件を追加するような場合、インデックスの張っていないカラムを検索条件に入れれば急激にパフォーマンスが悪化することもあるだろう。MySQLのようなDBMSだと特にシビアそうだ。


また、ジョインの順序など、細かいSQLの調整でパフォーマンスを上げたい場合もある。自動化されているとその辺りが心配ではある。


こういった事を解決するのに、一つは固定のSQLを差し込める機構を用意するなど、フレームワーク側に抜け道を用意するという方法がある。とても現実的な解決策だし、今は必要だと思う。


SQLを手書きするとして、じゃあそのSQLのチューニングをどうやってやるのって事だけど、それはスキーマや問い合わせ内容を元に人間が職人的な経験に基づいてやったりするのだろう。でもそれって全く自動化できない処理なんだろうか。


将来的に自動でチューニングすることを考えれば、問い合わせ処理全体をプログラム的に(メタ的に)扱える方が断然良いと思う。そしてそれは将来と言わずに今すぐ始めるべきだと思う。

*1:自分も以前「SQLを手で書くと手が汚れる」とか書いてたし。まあ冗談だけど。