はじめに
DataAccessObjectパターンによる開発でDAOジェネレータを使うケースが増えてきました。DAOジェネレータを使う目的は開発コストの削減です。しかし、リリースを急ぐプロジェクトはDAOジェネレータの使用方法をメンバに十分理解させないうちに開発を始めてしまいます。
Hibernateのような高度なO/RマッピングフレームワークであればOO(オブジェクト指向)およびRDB(関係データベース)に関する高度な知識が必要となります。ところが、平均的な技術者にそのような知識はありません。本来であれば、開発工数を削ってでも彼らの理解を確保するための教育をしなければなりませんが、予算と納期といった制約上の理由からそれがされないのが現状です。
DAOジェネレータは知識のある技術者にとっては便利でも、そうでない技術者にとっては学習コストのかかる厄介な代物であることを管理者は理解すべきです。殆どの技術者は後者ですから、DAOジェネレータを使う場合、相当な学習コストを覚悟する必要があります。学習を軽視すれば、コスト削減のために導入したはずのDAOジェネレータが逆にコストを生みプロジェクトは逼迫してしまいます。
DataAccessMethodパターンはこうした問題を解決します。更にこのパターンはDB仕様が変更された場合にも柔軟な解決方法を提供します。
本稿は、前稿『DAOパターンの欠点を補う「DataAccessMethodパターン」』の続きにあたりますので、併せてご覧ください。前回は概要について述べましたが、今回は設計よりの話題を中心に具体的に説明します。
対象読者
- 企業システム開発に携わっている方
- CJ2EEおよびGoFを理解されている方
背景
企業情報システムではRDBMSによる情報の永続化が行われるのが一般的ですが、RDBMSの仕様は各ベンダによって大きく異なります。そのため、プロジェクトに召集された開発メンバーがRDBMSの仕様について十分な知識を持っていなければなりません。もしも十分な知識を持っていなければ、プロジェクトリーダは彼らが設計を始める前に最低限の知識を習得させねばなりません。ところが、実際には予算や工期の制約があり、十分に教育することは難しいのが現状です。
こうした問題への解決策としてFacadeパターンがあります。これは「特殊なAPI」を使った処理をFacadeに集中させるというパターンです。Facadeパターンの本質は、その「特殊なAPI」に関する学習を誰か一人に押し付けるというプロセスにあります。Facadeパターンによりプロジェクトは下記の利点を享受できます。
- 特殊APIの隔離:学習コストを軽減する事が出来る
- リスクの隔離:問題が発生しても影響範囲を最小限に抑える事が出来る
CJ2EEのDataAccessObjectパターンはGoFのFacadeパターンの一種です。このパターンにはDataAccessObjectというFacadeに相当するクラスがあります。DataAccessObjectの開発は専門家でなければできないため、通常はDBMSの製品に精通した技術者が担当します。OracleやDB2の資格保有者や経験を積んでいるDBスペシャリスト等、データアクセスに詳しい上級開発者にデータアクセスロジックの開発を任せます。
もしかすると『DataAccessObjectをDAOジェネレータで自動生成できるなら、その分の人件費も削減できるはず』と信じている管理者がいるかもしれません。確かにDAOジェネレータによりDataAccessObjectは簡単に自動生成されます。しかし、DAOジェネレータを扱う開発者にはDAOジェネレータやDataAccessObjectに関する高度な知識が要求されます。それがなければ自動生成されたDataAccessObjectの品質は保証されません。
DataAccessObjectパターンの問題
DAOジェネレータの問題
DataAccessObjectパターンにはDAOジェネレータの問題があります。先に説明したようにDAOジェネレータは扱いが難しいのです。インピーダンスミスマッチや性能問題を解決するには高い専門知識と経験が必要です。その上で、開発効率、機能、性能、保守等がツールによりどのように改善されるのかを説明できる能力も必要です。ところが彼らのような専門家は少ないのです。
DAOジェネレータを上手く利用すれば品質の高いシステムを効率よく開発できます。大勢の上級技術者を安価で雇っているようなものです。しかし、彼ら、つまりDAOジェネレータには癖があります。彼らを扱えるのは彼らを良く知る者だけです。彼らを知らない非専門家や似非専門家が彼らをコントロールしようとしても結果は出ません。多くのプロジェクトはこの問題に苦しんでいます。
教育の問題
教育の問題というのもあります。DataAccessObjectを開発するメンバには経験が必要です。しかし、専門家を雇えばそういったメンバの教育の機会を奪います。そして、もしも専門家がプロジェクトから抜ければ、他のメンバーがタスクを引き継いだとしても経験不足が原因で低品質のシステムを納品してしまうかもしれません。せめて専門家を雇っている間に引継ぎ候補のメンバーを教育しておけば良いのですが、この教育も作業としての優先順位が低いプロジェクトが大半と思います。
これは「技術者なら独学で知識を身につけるのは当たり前だ」と突き放す考えがSI業界で支配的だからかもしれません。しかし、昔と違い今は何を学ぶべきかの選択肢が多い時代です。メンバにそのリスクを負わせるべきはありません。今の時代、教育コストはプロジェクトが負担するのが当然なのです。
DataAccessObjectパターンのような専門性の高い分野の教育は、研修による体系的知識習得と並行してOJT教育による経験を積ませるの良い解決策です。メンターによる徒弟制度は知識習得と経験を同時に満たす素晴らしい制度です。ただし、これも多くのプロジェクトでは施行は困難です。そういった、専門知識、経験、教育技術を兼ね備えたメンターが見つからないからです。
担当者の問題
開発効率面でDataAccessObjectは誰が開発すれば良いかという問題もあります。DataAccessObjectの仕様を良く知る専門家か、それともDataAccessObjectを直接必要とする各開発者か。後者を選択するプロジェクトが少なくありませんが、筆者は前者であるべきと考えます。
プロジェクトがDataAccessObjectパターンの専門家に担当を委ねたがらないのは、DataAccessObjectパターンの専門性が低いと評価しているからです。機能自体は単純なのだから、独学で習得できるはずと。DAOジェネレータがあるのだから、開発コストは殆どかからないはずと。そして、業務の専門化が開発する方が品質も高いはずと。しかし、全ての開発者が勤勉であるとは限りません。
勉強した者とそうしない者の開発効率は乖離します。1人の担当者に全てのDataAccessObject開発を委ねるべきか、全ての開発者に自分達に必要な分だけ開発させるべきか。DataAccessObjectがFacadeであれば前者であるべきは自明です。その場合1つの機能が複数の担当者により開発される事になります。こうした関心分離の開発をスライス開発といいます。
また、この手法は多少の管理コストがかかりますが柔軟性に優れています。しかし、広く認知されていないのでプロジェクトはこのスライス開発手法のリスクが高いと考えてしまい導入したがりません。知られていない新しい手法(関心の分離によりタスクを分割し振り分ける手法)より伝統的な手法(業務機能単位にタスク分割し振り分ける手法)を安全と考えてしまうのです。
仕様変更の問題
仕様は突如変わるものです。開発者間の認識のズレや実装可能性の低さは突如判明するものです。変更の積み重ねで品質(可読性、可変性、試験性、拡張性等)が低下することもあるでしょう。このような場合、クラスだけではなくテーブルの構造にも逐次最適が必要ですが、プロジェクトでは限界があります。
多くのプロジェクトで適用されるウォーターフォール型プロセスには逐次最適という概念はありません。「最初から正しく」という基本方針で、「戻りはない」という制約を守る事が推奨されます。たとえ戻りがあったとしても前工程までです。それ以上であれば予算が増大し納期が大幅に遅れるというリスクがあるからです。一般的に基本設計の修正期限は詳細設計工程までで、DB論理設計が実装工程に入ってから変更される事はありません。これを解決する手法がイテレーション型プロセスで、多くの迅速開発ではこの手法が適用されます。
ところが、イテレーション型プロジェクトでもDataAccessObjectパターンは仕様変更を阻害してしまうのです。DataAccessObjectパターンが仕様をロックしてしまうからです。これについては次節で解説します。
DataAccessMethodパターンによる解決
関心の分離
仕様変更に柔軟な構造にするには、機能を関心毎に分離する必要があります。このとき業務的抽象レベルの最も高いクラス(業務ロジックが定義されているクラス)がBusinessObjectです。この種のクラスは複雑であり、ジェネレータで定義する事は困難です。
一方、業務的抽象レベルが低いクラスは様々です。DBアクセス、ネットワーク、ファイルアクセス、キュー、ログ出力、バリデーションチェック、セキュリティ、権限、画面出力等々。これらは全て分離対象の関心事ですが、その中でも企業システムでは必ずといっていい程採用されるDBアクセスという関心を分離する事は極めて重要です。この種のクラスは単純であり、設定ファイルが明確であればジェネレータによる自動生成が可能です。
関心の隠蔽
DBの仕様変更が突如発生しても耐えられるようにDataAccessObjectの外部仕様をBusinessObjectから隠蔽します。これを可能にするためにDataAccessObjectとBusinessObjectの間にBoundaryObjectを設置します。BoundaryObjectはDataAccessObjectを抽象化した(つまり粒度を粗くした)BusinessObjectに委譲されるFacadeです。Facade(DataAccessObject)との間に更にFacade(BoundaryObject)を設置するわけです。
仕様変更はFacade(BoundaryObject)により吸収されるのでBusinessObjectへの影響は軽減されDB変更に強いシステムになるのです。
開発者の負担
このようにBoundaryObjectを設置することで可変性や保守性の向上を期待する事ができます。しかし同時に開発メンバにBoundaryObjectの定義という負担を強いることになります。作業だけでなく学習も大きな負担です。これを軽視して無理矢理BoundaryObjectを作らせても「無駄なクラスを開発している」という意識によりモチベーションは落ちるかもしれません。そうならないようにBoundaryObject導入による開発者の負担とシステムに与える効果を説明できなければなりません。
ハリウッドの原則
そこで開発者の負担を少しでも軽減するために、DataAccessMethodパターンを採用しBoundaryObjectをBusinessObjectの継承クラスとして定義します(委譲クラスとして定義する方法は後述します)。DataAccessMethodパターンでは、DataAccessMethodがBoundaryObjectに相当します。継承パターンは委譲パターンに比べ開発者の負担が軽減されます。
さらに、ハリウッドの原則が適用されるため依存性は逆転します。ハリウッドの原則が適用されたパターンとそうでないパターンの依存関係を比較します。
- 適用時:DataAccessMethod → BusinessObject
- 非適用時:DataAccessObject ← BusinessObject
ハリウッドの原則を適用する場合、BusinessObjectはDataAccessMethodに依存しません。つまりBusinessObject開発時にDataAccessMethodは必要ないのです。これは何を先に開発すべきかという問題です。DataAccessMethodを適用すればBusinessObjectを先に、DataAccessObjectを適用すればBusinessObjectを後に開発します。これにより仕様のロックという問題を解決する事が出来ます。