J2EEのベストプラクティス・トップ10(+2)(前編)

Kyle Brown, Senior Technical Staff Member, IBM Software Services for WebSphere
Keys Botzum, Senior Consulting I/T Specialist, IBM Software Services for WebSphere
Ruth Willenborg, Senior Manager, IBM WebSphere Performance
2004/7/23


主な内容
必ずMVCを使うこと
すべてのレイヤにテストツールを用意し、ユニットテストを自動化すること
アプリケーション・サーバの仕様ではなく標準仕様に基づき開発すること
J2EEセキュリティの利用を当初から検討すること
理解できる範囲でビルドすること


本記事は、IBM developerWorksからアットマーク・アイティが許諾を得て翻訳、転載したものです。

 J2EEのベストプラクティスについては、過去5年にわたり多数の文章が記されてきている。J2EEアプリケーションの開発ノウハウを解説した記事は数多く公表されており、出版された書籍もおそらく10を超えるだろう。J2EEに関する情報源はあまりにも多いため、互いに矛盾する方針を示していることもある。実際のところ、この“情報の迷路”こそが、J2EE導入における障害となっていたのである。

 筆者らは、この迷路でさまよえる読者に向けた簡単なガイドとして、J2EEにおける最も重要なベストプラクティスのトップ10リストを作成した。ただし、WebサービスもJ2EEの一部分として含めたところ、リストの項目は10を超えてしまった。そこでここでは、成長を続けるJ2EEに敬意を表し、トップ10の代わりに「トップ12リスト」とすることにしたい。

  1. 必ずMVCを使うこと
  2. すべてのレイヤにテストツールを用意し、ユニットテストを自動化すること
  3. アプリケーション・サーバの仕様ではなく標準仕様に基づき開発すること
  4. J2EEセキュリティの利用を当初から検討すること
  5. 理解できる範囲でビルドすること
  6. EJBコンポーネントを利用するときは、Session Facadeを常に使うこと
  7. ステートフルSession BeanではなくステートレスSession Beanを使うこと
  8. CMT(Container Managed Transaction)を使うこと
  9. プレゼンテーション手段としてはJSPを最優先に使うこと
  10. HttpSessionにはビジネス・ロジックに必要な最小限のデータだけを保管すること
  11. WebSphereでは、ダイナミック・キャッシング機能をオンにし、サーブレット・キャッシング・メカニズムを利用すること
  12. O/Rマッピングの手段として、まずはCMP Entity Beanを利用し、プログラマーの生産性を高めること

 前編では、上記のベストプラクティスのうちベスト5までを解説しよう。

1. 必ずMVCを使うこと

 ビジネス・ロジック(JavaBeansやEJBコンポーネント)とコントローラ(サーブレットやStrutsアクション)、プレゼンテーション(JSPやXML/XSLT)をそれぞれ明確に分離すること。こうしたレイヤ分けによって、さまざまな問題の発生を防ぐことができる。

 MVC(Model View Controller)モデルは、優れたJ2EEアプリケーションを設計するための基盤となるアーキテクチャだ。J2EEの導入を成功に導くうえでは、このMVCの利用に勝る方策はないといえるだろう。要するに、プログラムがこなすべき仕事を以下の3つに分割すればよいのである。

MVCモデル

プログラムがこなすべき仕事
ビジネス・ロジック
(モデル)
通常のJavaオブジェクトもしくはEnterprise JavaBeansによって実装される
ユーザー・インターフェイスのプレゼンテーション機能
(ビュー)
JSPとタグライブラリによって実装される。場合によってはXMLやXSLTも利用される
アプリケーションの画面フロー制御
(コントローラ)
Javaサーブレットもしくはそれに関連するクラス(Strutsアクションなど)によって実装される

 J2EEにおけるMVCの利用に関しては、優れた解説書がいくつか出版されている。とりわけ、Martin Fowler氏の「Patterns of Enterprise Application Architecture」(Addison-Wesley、 2002)、そしてKyle Brown氏の「Enterprise Java Programming with IBM WebSphere, 2nd Edition」(Addison-Wesley 、2003)の2冊は、このトピックについて広く深く掘り下げており、一読をお勧めしたい。

 MVCモデルを無視してJ2EEアプリケーションの設計を進めると、いくつもの問題が生じることになる。その大半は、ビューに多くを詰め込みすぎてしまうことに起因するものだ。例えば、小規模なアプリケーションでは、JSPの中で画面フローを制御したり、タグライブラリを用いてデータベースにアクセスしたりするものも多い。しかしこの方法では、開発が進むに従い、JSPのメンテナンスやデバッグが次第に困難になってしまうのだ。

 これとは逆に、本来はビューが担当すべき仕事をモデルに持たせている場合もしばしばある。よくある例としては、ビューで実装されているXMLのパース処理をモデルに移動してしまう状況が挙げられる。しかし、モデルはビジネス・ロジックの実行に専念すべきであり、ビューに依存する特定のプレゼンテーション処理を担当すべきではない。

 また、モデルとビュー、コントローラを単に設けただけで、レイヤ分けが不十分な場合もある。例えば、サーブレットとJSP、EJBによりMVCを構成しているものの、ビジネス・ロジックの多くをサーブレットで実行していたり、フロー制御をJSPが担当していたりするケースが多い。こうした間違いを避けるためには、コード・レビューやリファクタリングを厳格に実施すべきだろう。ビジネス・ロジックはモデル、フロー制御はコントローラに集中させる。そしてビューは、モデル内容をHTMLやJavaScriptで表現する処理に専念させるのである。

2. すべてのレイヤにテストツールを用意し、ユニットテストを自動化すること

 GUIのテストだけで済ませてはならない。すべてのレイヤでテストを実施することで、デバッグやメンテナンスが大幅に簡素化される。

 ここ数年、オブジェクト指向開発の分野では、大規模な変革が進みつつある。それは、XP(eXtreme Programming)やSCRUMなど、「アジャイル」と呼ばれる小回りの利く方法論の普及である。これらの方法論のほぼすべてに共通する特徴は、自動化されたテストツールの利用を徹底させている点だ。これにより、開発者が回帰テストに費やす時間を減らして生産性が高まるほか、不十分な回帰テストによるバグの残留を防ぐことができる。例えば、「テストファースト開発」と呼ばれるプラクティスでは、プログラム・コードを書き始める前にユニットテストを記述することで、テストの自動化を一段と推し進めている。

 コードのテストを実施するには、単体でテスト可能な部分を切り出さなくてはならない。コード全体が大きな塊のような状態で、複数の機能が絡み合っていると、個々の機能の正しさをテストすることは困難である。これに対し、MVCモデルをベースとするJ2EEアプリケーションでは、全体が複数のコンポーネントに分割されるため、それらを個別にテストするのが比較的容易である。例えば、EJBのEntity BeanやSession Bean、JSPなどに対し、個別のテストを記述するのは難しくはない。また、こうしたJ2EEのテスト作業をサポートするためのフレームワークやツールもいくつか提供されている。その例としては、junit.orgが開発したオープンソースのツール「JUnit」をはじめ、Apacheコンソーシアムのプロジェクトである「Cactus」などがある。いずれも、J2EEコンポーネントのテストにおいて大変役立つものだ。

 アプリケーションを隅々までテストすることのメリットは、各方面で指摘されている事実だ。それでもなお、GUI(WebベースもしくはJavaアプリケーションによるユーザー・インターフェイス)のテストだけをもってシステム全体のテストに代えてしまうプロジェクトは後を絶たない。

 GUIのテストだけでは不十分な理由は数多くある。その1つは、システムのコード全体を網羅するのが困難な点だ。GUIを通じて触れることができるのは、システムの一側面にすぎない。その背後には、バックグラウンド・ジョブやスクリプトなど、テストすべきさまざまな部分が存在する。しかし、いずれもGUIを持たないことが多いのである。2つ目の理由は、非常にきめの粗いテストしか行えない点だ。システムの振る舞いについてマクロなレベルでしかテストできないので、問題が発見されても対象範囲が広過ぎてバグの特定が容易ではない。

 3つ目は、GUIがすべて整うプロジェクトの後半にならないとテストを実施できない点。潜在的なバグのいくつかは、かなり後になってから発見されることになる。そして4つ目は、GUIテストではツールによる自動化が難しい点だ。GUIに何らかの変更が加えられるたびに、その部分を再テストするための修正作業も必要となる。これでは、十分なテストは行えない。一方、自動化されたユニットテストが整っていれば、変更による影響をほかの部分が受けていないことをすぐに確認できる。

 最後の理由は、ユニットテストの自動化によって、自動ビルドと簡単に統合できる点だ。これにより、システムの定期的な再ビルドの際(通常は夜間)に、人手を介すことなく回帰テストを実施できるのである。

 EJBやWebサービスによる分散コンポーネント・ベースの開発では、各コンポーネントの個別テストが不可欠なことも付け加えておきたい。GUIを持たないコンポーネントに対しては、きめ細かなテストを記述するしか方法はない。また、こうした手順を初めから導入しておけば、アプリケーション機能の一部を分散コンポーネントやWebサービスとして提供することになった場合でも、開発手順の大幅な変更に頭を悩ませずに済む。

 要するに、ユニットテストの自動化を徹底することで、アプリケーションの不具合をより早くより簡単に発見できる。そして、システマティックなテストによって、アプリケーション全体の品質が向上するのである。

3. アプリケーション・サーバの仕様ではなく標準仕様に基づき開発すること

 標準仕様を頭にたたき込み、よほどの理由がない限りそこから逸脱しないこと。「できること」と「すべきこと」の違いを理解すべし。

 J2EEという名の柵をむやみに跳び越えれば、そこには大きな落とし穴が待っている。J2EEを“少しだけ改善”するための行為が、パフォーマンスやポータビリティ(ベンダ間やバージョン間の移行)にかかわる深刻な問題を後になって引き起こすのである。事実、Wayne Beaton氏の記事では、マイグレーション作業におけるベストプラクティスとして、このルールが真っ先に挙げられている。

 必要以上に複雑なアプローチを取ることが致命傷となる場合も少なくない。よくある例としては、アプリケーション・サーバに内蔵されたJ2EE準拠のセキュリティ(認証や承認)機能を使わず、JAASモジュールでそれを置き換えてしまうケースがある。しかし、J2EE仕様が定める認証のメカニズムに手を加える作業は、特に慎重を期す必要がある。さもなければ、深刻なセキュリティ・ホールやベンダ間互換性の問題を招いてしまう。よって基本的には、J2EEアプリケーションの設計は、サーブレット仕様やEJB仕様に定められた認証機能に沿う形で行うべきである。そして、機能拡張が必要な場合は、標準のAPI(getCallerPrincipalメソッドなど)をできるだけ利用して実装する。この方法により、業務の要件に応じてベンダ製の強力なセキュリティ・インフラを利用したり、さらに複雑な認証ルールをサポートしたりできる。

 J2EEからの逸脱の例はほかにもある。J2EE仕様とは独立した永続化(データベース保存)機能の利用により、トランザクション管理の統合が困難になったケース。また、マルチスレッドやシングルトンなど、J2EEでの利用が制限されている機能に依存したケース。そして、JCAやJMS、Webサービスなどの標準技術を用いず、独自の手段でプログラム間通信を実装したケースなどである。これらのような設計では、他ベンダのJ2EEサーバへの移行時ばかりか単なるサーバのバージョン・アップ時にさえ、複雑なポータビリティの問題を引き起こしかねない。

 標準仕様からの逸脱は、標準仕様ではカバーできないことが明らかな場合にのみ限るべきだろう。例えば、EJB 2.1が登場するまでは、一定のスケジュールの下でビジネス・ロジックを定期実行する機能が標準化されていなかった。こうした場合は、ベンダが提供するソリューション(WebSphere? Application Serverエンタープライズ版のScheduler機能など)を用いるか、サードパーティ製ツールを利用すべきである。これにより、いずれ標準仕様に追加されたとき、ベンダによる移行サポートが期待できる。

 最後にもう一点。新しいテクノロジの導入を急ぎ過ぎないこと。J2EE仕様やベンダ製品にまだ統合されていないような技術を利用すると、時に悲惨な結果を招くこともある。なぜなら、ベンダ・サポートがまったく得られないからだ。ベンダがサポートを提供しない、標準化前のテクノロジに手を付けるべきではない。結局のところ、顧客のビジネスを実現することがわれわれのビジネスであって、目新しい技術をもてあそぶことではないのである。

4. J2EEセキュリティの利用を当初から検討すること

 J2EEセキュリティを利用すること。何はともあれ、すべてのWebコンテンツとEJBにセキュリティ保護を設定し、認証済みユーザーのみアクセス可能とすべし。

 筆者らの顧客の中にも、WebSphereに備わるJ2EEセキュリティ機能の利用をまったく検討していない例が少なからずあり、いつも驚かされる。同機能の導入を初めから検討しているのは、おそらく顧客全体の半分程度であろう。実際、大手の銀行や金融機関などの顧客がセキュリティ機能を使用していないケースもあったが、幸いなことに運用開始前のレビュー段階で対処することができた。

 ほとんどすべてのアプリケーションはセキュリティ機能を必要とする。よって、J2EEセキュリティを利用しないという選択は、J2EEベンダの製品より優れたセキュリティ・インフラを自前で開発できる、という危険なかけを意味する。しかし、分散アプリケーションのセキュリティ確保はとても厄介な問題だ。例えば、EJBへのアクセスを制限するには、ネットワーク上で安全に交換できる暗号化トークンを用いて実装しなくてはならない。われわれの経験からいえば、自作のセキュリティ・インフラは安全性が低く、実運用システムにおいて重大な脆弱性となり得る欠点を抱えている場合が多い(この問題については、Roland Barcia著「IBM WebSphere: Enterprise Deployment and Advanced Configuration」(IBM Press、2004)の第18章を参照のこと)。

 J2EEセキュリティを採用しない理由としてよく挙げられるのが、パフォーマンスの低下を抑えたい、外部のセキュリティ製品(例えばNetegrity SiteMinderなど)を利用するので特に問題はない、といった説明である。また、アプリケーション・サーバがどのようなセキュリティ機能を提供するのかが、よく理解されていない例もある。こうした落とし穴にはまってはならない。特に注意してほしいのは、たとえ外部のセキュリティ製品が優れた能力を備えていても、それはJ2EEアプリケーション全体の安全性を保証するものではない、ということだ。こうした製品とアプリケーション・サーバの両者を統合することで、初めてシステムのすべての側面でセキュリティを確保できる。

 また、別の理由として挙げられるのが、J2EEのロール・ベースのセキュリティ・モデルでは、複雑なビジネス・ルールの実装に求められるきめ細かなアクセス制御が行えない、という説明である。これには一理あるが、J2EEセキュリティを捨ててしまう理由にはならない。こうした場合は、要求される複雑なビジネス・ルールを実装する手段として、J2EEの認証とロールのモデルを活用すればよい。つまり、J2EEセキュリティによって容易に得られる信頼性の高い認証情報(ユーザーIDとロール)を用いて、複雑なビジネス・ルールを実装するコードを記述するのである。

5. 理解できる範囲でビルドすること

 イテレーティブな開発プロセスの導入によって、J2EEの各要素を段階的にマスターすべし。アプリケーション全体を一度に構築するのではなく、個々の小さな機能ごとにビルドすること。

 J2EEは巨大である。J2EEを初めて経験する開発チームにとっては、覚えるべき概念やAPIがあまり多過ぎるため、その全体を一度に学ぶことは極めて困難だ。こうしたプロジェクトで成否のカギを握るのは、焦らず一歩ずつJ2EEを学んでいくことである。

 具体的には、アプリケーションを構成する個々の小さな機能ごとにビルドを行えばよい。まずは、簡単なドメイン・モデルやバックエンドの永続化機能(例えばJDBCなど)を構築し、全体のテストを実施する。開発チームがこれらの作業に自信を持てるようになったら、続いてそのドメイン・モデルに対するフロントエンドをサーブレットやJSPで開発する方法を学び始める。また、EJBが必要と思われる場合は、最初にシンプルなSession Facadeを作成し、それをCMP Entity BeanもしくはJDBCベースのDAO(Data Access Object)と組み合わせる。Message Driven BeanやJMSといった複雑な機能へと進むのは、その後でよい。

 これは、それほど目新しいアプローチではない。しかし、実際にこの手順に沿ってチームのスキルを高めていく事例はあまり多くはない。大半のケースでは、スケジュール上の制約により、全体を一度に開発しようとする。MVCのビューとモデル、コントローラのすべてを同時に構築するといった具合だ。こうしたやり方の代わりに、アジャイルな開発手法の導入を検討すべきである。

 例えば、XP(eXtreme Programming)では、上述したようなインクリメンタルな学習と開発を推奨しており、その中でも「Model First」と呼ばれる手法がしばしば用いられる。これは、XPにおけるユーザー・ストーリーを整理し実装するために、ドメイン・モデルから先に作成するという手法である。つまり、主要なユーザー・ストーリーに対応するドメイン・モデルをまずは構築し、その後でユーザー・ストーリー全体の実装に必要なUIを追加していくという手順だ。この方法は、チーム全体がテクノロジを1つずつ学習していくのにとても都合がよい。いくつものトレーニングを並行して受けたり、何冊もの書籍を読んだりといった大変な思いをせずに済むのである。

 また、J2EEアプリケーションのレイヤごとにイテレーティブな開発を行うことで、適切なデザイン・パターンやベスト・プラクティスを導入しやすくなる。MVCのモデル部分から構築を進め、DAOパターンやSession Facadeパターンなどを随時適用していけば、JSPなどのビュー部分でビジネス・ロジックを実装してしまうこともないだろう。

 さらに、アプリケーションの個々の機能の単位で実装することで、パフォーマンス・チューニングを早期に始めるのが容易になる。Stacy Joinesが「Performance Analysis for Java Websites」(Addison-Wesley、2002)で指摘するように、パフォーマンス・テストをプロジェクトの最後まで遅らせることは大きなトラブルの原因となるのである。

 後編では、残りの5(+2)のベストプラクティスについて解説する。


本記事は「IBM developerWorks」に最初に掲載された「The top 10 (more or less) J2EE best practices」をアットマーク・アイティが翻訳したものです。

Java Solution全記事一覧





Java Agile フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Java Agile 記事ランキング

本日 月間