例題「伝言板システム」
ここでは例として、好きな時に好きなメッセージを残しておける、伝言板システムを作ることにしよう。
この伝言板システムは、オブジェクト指向言語であるJava言語で作ることにしよう。データベースは、リレーショナル・データベースを使う。
データ・モデル
データベースのデータ・モデル
リレーショナル・データベースを使うため、データベースのデータ・モデルは、リレーショナル・モデルとなる。
ここでは、次のER図(Entity-Relationship Diagram)のようなモデルになる。
冒頭の伝言板の例では、次のようなテーブルができる。
ビジネス・ロジックのデータ・モデル
オブジェクト指向言語であるJava言語で作るため、ビジネス・ロジックのデータ・モデルは、オブジェクト・モデルとなる。
ここでは、次のUML(クラス図)のようなモデルになる。
冒頭の伝言板の例では、次のようなオブジェクトができる。
デザインパターン
DAOパターン
データベースを使うシステムでは、一般に、データベースのテーブル定義とビジネス・ロジックとを切り離すために、DAOパターン(Data Access Objectパターン)を採用する。
DAOパターンを使わないシステムは、次のようなものになる。ビジネス・ロジックの中に直接SQL文が書かれるため、データベースのテーブル定義が変化すると、ビジネス・ロジックも修正しなくてはならない。
DAOパターンでは、SQL文の生成をDAOに閉じ、ビジネス・ロジック層に対しては、データベースのテーブル定義を隠蔽する。
トランスファー・オブジェクト
DAOパターンでは、DAOとビジネス・ロジック層の間でのデータの受け渡しに、トランスファー・オブジェクトを使う。
トランスファー・オブジェクトは、バリュー・オブジェクトとも言い、自分自身では永続化の処理を持たない、単純なデータである。O/Rマッピング・フレームワーク「Hibernate」では、エンティティ・クラスと呼ばれるものが、これに該当する。
インターフェースの設計
DAOパターンを使うシステムでは、以下の2点について考える必要がある。
- DAOのインターフェース
- トランスファー・オブジェクトが持つデータ
伝言板システムでは、DAOのインターフェースとして、下記の3つのメソッドを用意することにしよう。
- 伝言を1つ追加する。(add伝言)
- 伝言を1つ更新する。(update伝言)
- すべての伝言を取得する。(getAll伝言)
これらのメソッドで受け渡すトランスファー・オブジェクトを、2つのアプローチで考えてみよう。
ボトム・アップの設計
データベースのモデル(リレーショナル・モデル)をベースに設計すると、次のようなトランスファー・オブジェクトができる。
このアプローチでは、トランスファー・オブジェクトに、リレーショナル・データベースにおけるキーを持たせる点が特徴である。
このトランスファー・オブジェクトを使って、DAOのインターフェースを次のように定める。
しかし、この設計には、次のような問題がある。
- 新しい伝言を追加する際にも、伝言番号を受け渡している。
- 実際には、伝言番号はデータベースで自動的にユニークな値が付けられる。
- 伝言を1つ更新する際には、伝言番号だけは書き換えてはいけない。
- もし伝言番号を書き換えてしまうと、取得した伝言とは別の伝言を勝手に書き換えてしまう。
受け渡しに使うトランスファー・オブジェクトのデータの中に、指定すべきものと指定しなくて良いもの、変更して良いものと変更してはいけないものが混在してしまうため、分かりにくい。どれを書き換えて良いのかは、暗黙の了解になってしまう。
また、次のような問題もある。
- すべての伝言を取得しても、利用者の名前を表示できない。
- すべての伝言だけではなく、すべての利用者も取得する必要がある。
トップ・ダウンの設計
ビジネス・ロジックのモデル(オブジェクト・モデル)をベースに設計すると、次のようなトランスファー・オブジェクトができる。
このアプローチでは、トランスファー・オブジェクトどうしが、直接、参照し合う点が特徴である。
このトランスファー・オブジェクトを使って、DAOのインターフェースを次のように定める。
しかし、この設計には、次のような問題がある。
- 伝言を1つ更新する際に、リレーショナル・データベースにおけるキーが渡されない。
- データベースに記録されている伝言のうち、どの伝言を更新するのか分からない。
- 伝言を追加または更新する際に、利用者の名前しか渡されない。
- 利用者IDが分からないため、データベースに記録できない。
受け渡されるオブジェクトと、データベースのレコードとの対応関係が失われているため、データベースの更新ができない。
インターフェースの歪み
インピーダンス・ミスマッチ
DAOは、データベースのモデル(リレーショナル・モデル)と、ビジネス・ロジックのモデル(オブジェクト・モデル)との間で、データの受け渡しを仲介する存在である。
だが、そこで受け渡すトランスファー・オブジェクトは、リレーショナル・モデルをベースに設計しても、オブジェクト・モデルをベースに設計しても、どちらのアプローチでも問題が生じる。
このように、リレーショナル・モデルと、オブジェクト・モデルは、うまくかみ合わない。これを、インピーダンス・ミスマッチと呼ぶ。
シャドウ・インフォメーション
この例では、リレーショナル・データベースにおけるキーの扱い方で問題が生じた。
オブジェクト・モデルには、キーという概念は登場しない。しかし、データベースにアクセスするためには、キーは必須である。
このように、設計では現れないのに、実装時には必要になってしまう情報を、シャドウ・インフォメーションと呼ぶ。
シャドウ・インフォメーションは、そもそもモデルに登場しないため、オブジェクト・モデルからはうまく扱えない。トランスファー・オブジェクトが持つデータのうち、キーだけは、指定しなくても良い、変更してはいけない、といった暗黙の了解が生じてしまったのは、そのためだ。
この他に、クラスの継承を使ったオブジェクト・モデルをデータベースに保存する場合にも、インピーダンス・ミスマッチが起こり、シャドウ・インフォメーションが必要になる。
O/Rマッピング
データベースのモデル(リレーショナル・モデル)と、ビジネス・ロジックのモデル(オブジェクト・モデル)という、異なる2つのデータ・モデルを結びつけることを、O/Rマッピングと呼ぶ。
トランスファー・オブジェクトと、DAOのインターフェースは、O/Rマッピングの方法によって、異なったものとなる。
2つのデータ・モデルの特徴を兼ね備えたトランスファー・オブジェクトを使う
一般的には、下記に挙げた、トップ・ダウンの設計と、ボトム・アップの設計の、双方の特徴を兼ね備えたトランスファー・オブジェクトを使うことが多い。
- トランスファー・オブジェクトに、リレーショナル・データベースにおけるキーを持たせる。
- トランスファー・オブジェクトどうしが、直接、参照し合う。
この方法では、次のようなトランスファー・オブジェクトを作る。
このトランスファー・オブジェクトを使って、DAOのインターフェースを次のように定める。
O/Rマッピング・フレームワーク「Hibernate」でエンティティ・クラスを自動生成する場合も、この方法になる。
しかし、この方法では、ビジネス・ロジックではシャドー・インフォメーションを使用しない、という、暗黙の了解が必要になってしまう。
シャドウ・インフォメーションを明示的に使う
シャドウ・インフォメーションに伴う暗黙の了解を無くすために、リレーショナル・データベースにおけるキーを、ビジネス・ロジックでも明示的に扱う、という方法も考えられる。
この方法では、次のようなトランスファー・オブジェクトを作る。
新しい伝言を追加する際に指定すべきデータ、伝言を更新する際に変更しても良いデータだけが、1つのオブジェクトになっている。
このトランスファー・オブジェクトを使って、DAOのインターフェースを次のように定める。
この方法では、DAOのインターフェースでも、リレーショナル・データベースのキーを積極的に使用する。
このようにすると、暗黙の了解が無い、明確なインターフェースが定義できる。
但し、この方法では、データベースのモデル(リレーショナル・モデル)を、そのままビジネス・ロジックに公開している。
ビジネス・ロジック層のデータ・モデルをオブジェクト・モデルとし、DAOの役割を、単にデータベースのテーブル定義を隠蔽するだけではなく、パーシステンス層との境界でモデルの変換を行うもの、と考えるのであれば、この方法は望ましくない。
シャドウ・インフォメーションに伴う暗黙の了解を無くすために、シャドウ・インフォメーションをインターフェースで隠蔽する、という方法も考えられる。
トランスファー・オブジェクトが持つデータのうち、ビジネス・ロジックで必要とされるデータのみを参照できるように、アクセスを制限したインターフェースを用意する。ビジネス・ロジックでは、トランスファー・オブジェクトそのものではなく、アクセスを制限したインターフェースを通じて操作する。
この方法では、トランスファー・オブジェクトの実体としては、前述した、一般的なトランスファー・オブジェクトを使う。
トランスファー・オブジェクトの実体は、リレーショナル・データベースにおけるキーを持つ。一方、ビジネス・ロジックが使うインターフェースでは、キーを取得することはできるが、キーの値を書き換えることはできない。つまり、キーをRead Onlyな属性としている。
このトランスファー・オブジェクトを使って、DAOのインターフェースを次のように定める。
このようにすると、暗黙の了解が無い、明確なインターフェースが定義できる。
しかも、ビジネス・ロジックから見ると、トランスファー・オブジェクトやDAOのインターフェースは、リレーショナル・データベースにおけるキーが登場しない、オブジェクト・モデルになっている。
そのことを、データベースから取得した伝言の内容を書き換えて、データベースに保存し直すケースで確かめてみよう。
DAOでは、トランスファー・オブジェクトの実体である「伝言Implクラス」のインスタンスを生成して、そこに、リレーショナル・データベースに記録されているキーの値をセットしてから、ビジネス・ロジックに渡す。
ビジネス・ロジックでは、DAOから受け取った伝言のインスタンスの内容を書き換える。この際、ビジネス・ロジックは「伝言インターフェース」を通じてしか操作できないため、トランスファー・オブジェクトの実体が持つキーの値は変わらない。
ビジネス・ロジックは、内容を書き換えた伝言のインスタンスを、DAOに渡す。
DAOでは、ビジネス・ロジックから受け取った伝言のインスタンスが、「伝言Implクラス」のインスタンスかどうかは分からない。しかし、「伝言インターフェース」の「get伝言番号」メソッドによって、リレーショナル・データベースにおけるキーを得、該当するレコードの内容を更新することができる。