画面遷移定義におけるJSFとSeamの思想の違いについて

以前にSeamのバイジェクションと一般的なDIの違いにて記述したバイジェクションと並んでSeam固有の機能として、pages.xmlを使ったページ指向の画面遷移定義があると思います。一見すると普通の(冗長で悪名高き)JSFのfaces-config.xmlをちょっと簡潔にしたくらいと思って重要な違いを見過ごしてしまいがちですが、実は両者で思想が結構異なるのだということに最近少しずつ分かってきました。

ルール指向かページ指向か

まず、JSFのfaces-config.xmlの場合は、定義ファイルの構造からも明らかですが、画面遷移ルールの羅列になっています。あくまでも個々の単位が遷移ルールなのであり、画面遷移のルールベースを定義するための設定という感じです。

<navigation-rule>
ルール1
</navigation-rule>
<navigation-rule>
ルール2
</navigation-rule>
...

一方で、pages.xmlの方は、ファイル名の通りページ定義の集まりとして個々のページの定義の集合となっているのです。画面遷移はそれぞれのページに含まれる属性という感じ。

<pages>
  <page view-id="/page1.xhtml">
    ページ1の定義
  </page>
  <page view-id="/page2.xhtml">
    ページ2の定義
  </page>
...
</pages>
...

Seamのpages.xmlでは、ページ単位に定義を分けることで、画面遷移ルール以外にもページパラメーターやページアクションなど、個々の画面単位に定義すべきメタ情報を自然に定義しやすくなっています。また、画面遷移はページに含まれる属性として捉えます。「ページ」という概念を重要な存在として考えているということであり、よりオブジェクト指向的です。この違いを理解することで、複雑なSeamの画面遷移設定ファイルの意味がわかりやすくなると思います。
もちろん、前者でもルールをfrom-view-idごとに分けて書くことができますし、後者でもview-idにワイルドカードを指定することができるなど、一見両者の違いが見えにくくなっているところはあります。
なお、JSF2.0ではページパラメーターなどSeamの機能がたくさん取り入れられてはいるものの、このページ指向の設定ファイルは取り入れられていません。バイジェクションと同様にSeam2.2までで終わりになってしまうのでしょうか?

アクションの戻り値がStringのoutcome値に限定されない

JSFでは基本的にアクションBean(管理Bean)は、Web層専用のクラスであり、各アクションメソッドの戻り値は遷移先の論理的な分岐名をあらわす文字列となります。(ただし、JSF2.0ではビューIDを直接指定可能)Seamの場合は、そういった制約はありませんし、アクションBeanをEJBとして業務層のセッションBeanで実装するような場合、むしろoutcome文字列を返すのは不自然な設計となります。Seamの場合はpages.xmlの記述力がfaces-config.xmlと比べてはるかに柔軟になっていることで、従来の画面遷移専用のJSFのアクションBeanの責務をほとんどなくすことが可能となっています。(xmlよりもなるべくJavaで記述した方が便利かもという意見もあるかと思いますが。)

GETによる画面遷移を大幅強化

JSF1.2までの仕様では、GETリクエストでパラメーターを渡しながらボタンやリンクをクリックして画面遷移することはほとんど想定されていません。実際GETリクエストは画面の初期構築をするためのものとの前提であり、業務ロジックの呼び出しは基本的にPOSTバックで行うことになっています。つまり、通常のWebアプリケーションで当たり前のように必要な一覧画面の行をクリックしてIDをパラメーターで渡し、GETで詳細画面に遷移させるといったような機能の実現が想像以上に困難でした。特にJSF標準の機能を使いブックマーク可能なURLを実装することはほぼ不可能です。Seam in Action: Covers Seam 2の3章でJSFの「Everything is a POST」問題として力説されています。Seamでは

  • ページアクション
  • ページパラメーター
  • タグ

などのサポートにより、GETによる画面遷移を可能にしています。逆に、この改善点をうまく利用できないとSeamの魅力をほとんど活用できていないことになると思います。ただし、GETとPOSTの使い分けとか、基準を正しく決めて標準化することは実際それほど簡単なことではありませんが、ある程度チームのアーキテクトがパターン化しておくことが大切だと思います。