DAOの悪夢 - PHPでデータベースを扱う(1)

Posted by Hiraku on 2012-06-03

最近、昔の自分が書いたコードをメンテしているのですが、何というか、「ええい、誰じゃこのコードを書いたのは!!」と叫んでは「…4年前の俺でした…」とセルフツッコミを繰り返しています。すごく読みにくいコードで、ストレスたまりまくりです。そのため、「今ならどう書くか」をよく考えました。ちょっと長くなるかもしれませんが、アンチパターンとして解説したいと思います。

DAOパターンについて

私が鬱々としてメンテしているコードですが、データベースとのやり取りを行うためのクラスです。当時意識していませんでしたが、改めて見ているとDAOパターンを再現しているものでした。

DAO(Data Access Object)とはデザインパターンの一種で、データベースへのアクセスロジックを集約したクラスのことです。有名なGang of Fourによる23種の基本デザインパターンには直接含まれていませんが、Facade(ファサード)パターンをデータベース向けに使ったものと考えることができます。

アプリケーションは、必要になったらその都度MySQLに接続したりせず、データベースとのやり取りは全てDAOを介して行います。擬人化すれば「データベースのスペシャリスト」である「DAO君」がいて、その人にデータベースへの保存やら検索やらを仲介してもらうようなイメージです。

DAOのイメージ

何でも嫌な顔一つせずに引き受けてくれるDAO君。きっと優秀でさわやかでイケメンなのでしょう。

DAOパターンによって、アプリケーション側はデータベースの小難しい仕様を意識しなくてよくなります。SQLはDAOクラス中にしか登場しない状態です。

データベースに関する操作を一か所に集約することで、ソースコードの検索など、メンテナンス性が向上します。もしも「使うDBMSをMySQLからPostgreSQLに変えたい」「データベースより先にmemcachedを検索したい」などの無茶な仕様変更が後からあったとしても、DAOに集約されているのでDAOを修正するだけで済みます。変更にも強くなるわけですね。

DAOに足りないもの

こうしてメリットだけ並べるととても良さそうなデザインパターンに思えてきます。実際、べたーっとDB関連のコードが書かれているのに比べれば何十倍もメンテナンス性は向上します。そう、発想は正しい。しかし想像力が足りていないのです。そこがDAOの問題です。

ちなみに私がメンテしているシステムには、データベースのテーブルが20個ほど存在します。…この時点で想像しておくべきだった…!

安直にDAOを作るなら、一つのSQL文につき一つのメソッドが必要になります。そしてテーブル一つにつき、最低でもCRUD…すなわちINSERT, SELECT, UPDATE, DELETEの4つが必要なわけですから、20テーブル × 4 = 80ものメソッドが必要になるのです。

<?php
class DAO {
    function __construct() {
        /* DB接続のコード */
    }
    function findUserById($id) { /* ... */ }
    function findUserByName($name) { /* ... */ }
    function insertUser(array $user) { /* ... */ }

    /* こんなのが80メソッド以上つづく */

}

しかも、80というのは最低限の見積もりです。実際にはさまざまな条件でのSELECTがあるはずですから、こんな数では収まりません。全部DAO内に実装していくと、メソッド数はとんでもない数に膨れ上がり、メンテナンス性は落ちていきます。

あと、これだけ巨大になるとメソッドのインターフェースを統一するのが難しく、「このメソッドは日付をstringで渡すけど、こっちのメソッドはunixtimeとしてint型を渡さないと動かない」みたいな齟齬が生まれやすくなります。DAOを使う側のアプリケーションも汚くなっていきます。

それに、これはデータベースをあくまでデータとして扱っています。ドメインロジックを実装する場所がないのです。例えば身長と体重を持つ「student」テーブルがあったとして、「BMIを計算する」などのロジックはどこか別のところに定義しなくてはいけません。これではオブジェクト指向とは言えません。

…とまあ、いろいろと問題が出てくるのです。

クラスの分割方法に解を持たないDAO

当然、それならばクラスを分割しましょうということになるのですが、これがなかなか難しい。DAOパターン自体は、「データベースアクセスは一か所にまとめるべし」としか書いておらず、分割方法はDAOパターンから読み取ることはできません。

DAOを分割してメンテしやすいコードにするためには、別のデザインパターンが必要です。結局、4年前の私はここに到達する前に挫折し、DAOクラスをぶくぶくと太らせる方向に逃げてしまいました。


まとめ

データベース操作のロジックはなるべく一か所にまとめるべきである。この方向性自体は正しい。

しかしDAOパターンだけではメンテナンス性の高いコードにはならない。もっと踏み込んだデザインパターンが必要。


データベースアクセスをどう実装するべきか。次回、もう少し考えてみたいと思います。

keyword: PHP DAO デザインパターン

PHPの最新記事