それでは、上記で発生要因として推測された項目に、どう対処すればよいかを考えてみます。発生要因をさらに簡潔に整理してみると、問題(1)の根本原因は以下の2点になります。
まず、1つ目の原因である「レイヤごとの役割・責任範囲が実装可能なレベルまで詳細に明確化されていない」について、具体的に解決法を探ってみましょう。解決する方法は一言でいってしまえば、「役割・責任範囲を明確にすること」ですが、ビジネスロジック・レイヤとデータベース・レイヤに関する役割を一例として挙げてみます。表2「レイヤ別の役割・責任範囲の定義例」を参照してください。
役割・責任範囲を決めていく詳細な手順については、第3回の記事で解説する予定なので今回は割愛しますが、このように、レイヤごとの役割・責任範囲を明確にすることで、レイヤ間で齟齬(そご)がある設計・実装に陥る可能性は低くなると考えられます。ただし、役割・責任範囲は全体(開発プロジェクト全体またはレイヤ間同士)で同意が取れなければ意味がなくなってしまいます。これは、非常に重要なポイントとなります。
役割・責任範囲 | |
---|---|
ビジネスロジック・レイヤ | 業務機能を実現するための業務ロジックを設計・実装する |
データベース・レイヤが実装したトランザクション用のメソッド(begin()、commit()、rollback()、end())を使用し、業務トランザクションの制御を設計・実装する | |
業務トランザクション内のエラーハンドリングを設計・実装する | |
ビジネスロジックで扱う業務データモデルに準じたエンティティ・クラスを設計・実装する | |
データベース・レイヤに必要なデータベース問い合わせ処理の要件(どういったデータの問い合わせ処理が必要か)を提示する | |
データベース・レイヤ | ビジネスロジック・レイヤにより要件として提示されたデータベース問い合わせ処理(Select、Update、Delete)を設計・実装する |
O/Rマッピング(データベース・データモデルと業務データモデルとのマッピング)を設計・実装する | |
トランザクション用メソッド(begin()、commit()、rollback()、end())を設計・実装する。 | |
コネクションプールなどのConnection制御アーキテクチャも設計・実装する | |
表2 レイヤ別の役割・責任範囲の定義例レイヤ別の役割・責任範囲の定義例 |
次に2つ目の原因「各レイヤ間に強い依存が発生している(密結合状態)」について、具体的に解決法を探ってみましょう。これを解決するためには、「レイヤのカプセル(隠ぺい)化」が必要になります。カプセル化とは、オブジェクト指向で出てくるカプセル化の概念とほとんど同じ意味になります。オブジェクト間の依存を極力なくし、データの隠ぺい性の保証およびクラスの拡張性の確保を目的としているオブジェクト(クラス)のカプセル化と同様に、レイヤ自体をカプセル化することにより、各レイヤ同士で依存の少ない疎結合の状態にすることが可能になります。カプセル化の方法は次項で説明します。
レイヤのカプセル化を実現する方法は、実装要件が実現可能であることを前提として、各レイヤ間でデータを受け渡しするインターフェイス(例えていえば、玄関です)を抽象化(集約)することです。インターフェイスを抽象化(集約)する理由は、必要以上のインターフェイスの存在は、メンテナンス性の低下を招くと同時に、必然的にレイヤ間の依存も強くしてしまう可能性があるからです。図4を参照してください。この例では、業務機能ごとにデータベース・レイヤが1対1の関係でインターフェイスを用意しています。これは、ビジネスロジック・レイヤ側にデータベース・レイヤのすべての処理をさらけ出しているのと同義になり、依存関係を強く持った実装になってしまう可能性が高いと考えられます。
どういった設計が望ましいかを理解するために、図5を参照してください。
この例では、データベース・レイヤのインターフェイスを抽象化することで1つに集約させ、データベース・レイヤのDAO実装クラスは、このインターフェイスを実装する形でFactoryパターン注1 により設計・実装しています。このようにインターフェイスを抽象化(集約)させデータベース・レイヤをカプセル化することで、問題(1)の解決だけでなく、以下のことが実現可能になります。
(次ページに続く)
Copyright © ITmedia, Inc. All Rights Reserved.