連載


Webと企業システムを結ぶ実践的アーキテクチャ
企業システムのeビジネス化における課題と解決法を探る

斎場正弘
日立製作所
2001/9/22

第2回 最適なフロントエンド開発手法

今回の内容

最適なアプリケーションモデル
標準的な画面遷移パターン
画面と業務ロジックの並行開発手順
画面遷移設計の注意点

 第1回では、Webアプリケーションサーバシステムの概観を述べました。今回は、Webアプリケーションのフロントエンド(画面系)の開発に焦点を絞り、その手法を説明します。ECサイトを中心としたWebアプリケーションに見られるように、Webアプリケーションに求められる要件として以下の内容が挙げられます。

  • アトラクティブな画面デザイン
  • 操作が容易な画面遷移

 Webアプリケーションのフロントエンドは、エンドユーザーとの接点となる非常に重要な部分です。このため、フロントエンド部分の仕様については、デザイナーを交えて検討されるケースが多く、仕様が確定するまではある程度の時間が必要です。

 特に、画面デザインについては、システム稼働直前まで仕様の変更が発生する場合も少なくありません。一方、Webアプリケーションは、最新の情報をより早くお客さまに提供するといった業務の性質上、企画から稼働までの期間が非常に短い傾向があります。こうした中でのWebアプリケーションの開発スタイルの1つに画面と業務ロジックの並行開発があります。

 今回は、画面と業務ロジックの並行開発を中心に、Webアプリケーションのフロントエンド部分の開発方法を紹介します。


 最適なアプリケーションモデル
―画面と業務ロジックを分離する最適なアーキテクチャ―

 Webアプリケーション開発自体での1つのソリューションとして、前述したように画面と業務ロジックの並行開発が挙げられます。以下に、J2EEアプリケーションサーバをベースにしたWebアプリケーションモデルの典型例を示します(図1)

図1 J2EEアプリケーションサーバでのWebアプリケーションモデル

 このアプリケーションモデルでは、入力画面用JSPと出力画面用JSPに対して、入力画面情報および出力画面情報を共にプロパティとして持つJavaBeansを、業務ロジックが記述されるEJB(Enterprise JavaBeans)の前に配置しています。制御用JSPは、入力画面の入力情報をJavaBeansの入力プロパティに設定し、JavaBeans中のメソッドを呼び出します。JavaBeansのメソッドでは、EJBを呼び出すかJDBCを介することでデータベースにアクセスし、その結果を出力プロパティに設定します。制御用JSPは、JavaBeansのメソッドから制御が戻ると、出力プロパティからデータを取り出し出力画面用JSPに設定することで、出力画面に結果が表示されます。

 このモデルを以下の入出力画面のサンプルコードをもとに説明します(図2)。この画面遷移は、入力画面中のテキストフィールド、パスワードフィールドに値を設定し、サブミットボタンを押下すると入力情報が出力画面中に埋め込まれ表示されるという非常に簡単なものです。

図2 入出力画面のサンプル

 この入出力画面における、入力画面用のJSPコードService1In.jspリスト1のとおりです。行番号11のACTIONには、出力画面用のService1Out.jspが指定されています。

 テキストフィールドの値がinProperty0として、パスワードフィールドの値がinProperty1として出力画面用のJSPに渡されます(行番号13、15を参照)。

 図1で紹介したWebアプリケーションモデルでは、制御用JSPはJavaBeansおよび出力画面用JSPより先に実行されますが、このしくみについては、出力画面用JSPの説明で詳しく解説します。なお、ACTION に指定されている<%= response.encodeURL(actionUrl) %>中のHttpServletResponse#encodeURL(String url)は、ブラウザがcookie をサポートしていない場合に、セッションIDを付加したURLを返すものです。

リスト1 入力画面用JSPコード
1 <%@ page contentType="text/html; charset=Shift_JIS" %>
2 <%@ page import="java.util.*" %>
3 <%@ page import="javax.swing.*" %>
4 <%@ page import="javax.swing.table.*" %>
5 <HTML>
6 <HEAD>
7 <TITLE>Service1</TITLE>
8 </HEAD>
9 <BODY>
10 <% String actionUrl = "Service1Out.jsp"; %>
11 <FORM ACTION="<%= response.encodeURL(actionUrl) %>" METHOD="POST">
12   入力情報0<BR>
13   <INPUT TYPE="text" NAME="inProperty0" SIZE="20" > <BR>
14   入力情報1<BR>
15   <INPUT TYPE="password" NAME="inProperty1" SIZE="20" > <BR>
16 <H2 ALIGN="center">
17 <INPUT TYPE="submit" VALUE="Submit">
18 <INPUT TYPE="reset"VALUE="Reset">
19 </H2>
20 </FORM>
21 <BR>
22 </BODY>
23 </HTML>

 出力画面用のJSPであるService1Out.jspリスト2のとおりです。行番号6に<% @include file="Service1Logic.jsp" %>とありますが、このService1Logic.jsp図1のWebアプリケーションモデルでの制御用JSPに相当するものです。

 行番号7以降が出力される前にService1Logic.jspが実行され、その中でJavaBeansのあるメソッドが実行されることになります(リスト3、リスト4参照)

 Service1Logic.jsp中で行番号14、17で使用されている変数のoutProperty0outProperty1に値が設定されるため出力画面中のそれぞれの値が表示されることになります。

リスト2 出力画面用JSPコード
1 <%@ page contentType="text/html; charset=Shift_JIS" %>
2 <jsp:useBean id="beanService1" scope="request" class="servicesample.business1.Service1" />
3 <%@ page import="java.util.*" %>
4 <%@ page import="javax.swing.*" %>
5 <%@ page import="javax.swing.table.*" %>
6 <%@ include file="Service1Logic.jsp" %>
7 <HTML>
8 <HEAD>
9 <TITLE>Service1出力</TITLE>
10 </HEAD>
11 <BODY>
12 <FORM METHOD="POST">
13   出力情報0<BR>
14   <INPUT TYPE="text" NAME="outProperty0" VALUE="<%= outProperty0 %>" SIZE="20" >
15   <BR>
16   出力情報1<BR>
17   <INPUT TYPE="text" NAME="outProperty1" VALUE="<%= outProperty1 %>" SIZE="20" >
18   <BR>
19 </FORM>
20 <% String Service1InUrl = "Service1In.jsp"; %>
21 <FORM ACTION="<%= response.encodeURL(Service1InUrl) %>" METHOD="POST">
22 <INPUT TYPE="submit" VALUE="入力ページへ">
23 </FORM>
24 </BODY>
25 </HTML>

 制御用のJSPであるService1Logic.jspは以下のとおりとなります(リスト3)。行番号3〜11は、入力画面用JSP(リスト1参照)から渡されたテキストフィールドの値を取り出す処理が記述されています。行番号12で、JavaBeans中の当該入力プロパティに渡されたテキストフィールドの値を設定しています。同様に、行番号14〜22は、入力画面用JSP(リスト1参照)から渡されたパスワードフィールドの値を取り出す処理が記述されています。行番号23で、JavaBeans中の当該入力プロパティに渡されたパスワードフィールドの値を設定しています。なお、行番号7、18でのエンコーディング変換の記述は、入力データをISO8859_1へと変換するWebコンテナ(JSP/Servletエンジン)の場合に必要となります。

 JavaBeans中の入力プロパティに入力データを設定すると、行番号25でJavaBeansの当該メソッドservice1Method()を呼び出します。JavaBeansの当該メソッドでは、入力プロパティをもとに処理を実施し、結果を出力プロパティに設定してリターンします。行番号26〜38は、当該メソッドの戻り値および例外のthrowに従って、それぞれのエラーページへ制御を渡す処理が記述されています。行番号42、43では、当該メソッドから正常リターンした場合に、JavaBeans中の出力プロパティから結果を取り出し、出力画面用JSP中の出力用変数に設定しています。

リスト3 制御用JSPコード
1 <% {
2   /* 入力情報0パラメタ解析処理 */
3   String inProperty0 = "";
4   String [] w_inProperty0 = request.getParameterValues("inProperty0");
5   if ((w_inProperty0 != null) && (w_inProperty0[0].length() != 0)) {
6     try {
7       inProperty0 = new String(w_inProperty0[0].getBytes("8859_1"), "Shift_JIS");
8     } catch(Exception e) {
9       e.printStackTrace();
10    }
11  }
12  beanService1.setInProperty0(inProperty0);/* 入力データをJavaBeanに設定 */
13  /* 入力情報1パラメタ解析処理 */
14  String inProperty1 = "";
15  String [] w_inProperty1 = request.getParameterValues("inProperty1");
16  if ((w_inProperty1 != null) && (w_inProperty1[0].length() != 0)) {
17    try {
18      inProperty1 = new String(w_inProperty1[0].getBytes("8859_1"), "Shift_JIS");
19    } catch(Exception e) {
20      e.printStackTrace();
21    }
22  }
23  beanService1.setInProperty1(inProperty1); /* 入力データをJavaBeanに設定 */
24  try {
25    boolean rc = beanService1.service1Method(); /* JavaBeanメソッドを呼び出す */
26    if (!rc) {
27      String fwjForwardPage = "/Service1Err0.jsp";
28  %>
29      <jsp:forward page="<%= fwjForwardPage %>" />
30  <%
31      }
32    } catch (Throwable t) {
33      t.printStackTrace();
34      String fwjForwardPage = "/Service1Err1.jsp";
35  %>
36      <jsp:forward page="<%= fwjForwardPage %>" />
37     <%
38    }
39  } %>
40  <%-- 出力ページで使用するプロパティの情報 --%>
41  <%
42    java.lang.String outProperty0 = beanService1.getOutProperty0();
43      /* 出力デ-タにjavaBenas出力プロパティ値を設定 */
44    java.lang.String outProperty1 = beanService1.getOutProperty1();
45      /* 出力デ-タにjavaBenas出力プロパティ値を設定 */
46 %>

 JavaBeans (Service1.java)はリスト4のとおりです。行番号8〜22は、入力画面からの入力データが格納されるプロパティおよびsetter、getterメソッドが記述されています。行番号29〜34は、制御用JSPから呼び出されるメソッドが記述されています。また、行番号36〜50は、出力画面へ出力するデータが格納されるプロパティおよびsetter、 getter メソッドが記述されています。メソッドでは、入力プロパティをもとに処理を実施し、出力プロパティに値を設定するロジックを記述することになります。このサンプルでは、入力データを入力プロパティから取り出し、出力プロパティに設定するロジックが記述されています。

 このJavaBeansの構造は、入力画面および出力画面を1つのセットとして、入出力データが格納されているため、業務において意味ある固まりとなっています。従って、例えば制御用JSPでHttpSessionをJavaBeansにプロパティで渡し、メソッド中でこのクラス自体(this)をHttpSessionに格納しておくことで、後続のセッションで前の入出力情報を参照することも容易です。

リスト4 JavaBeansコード
1  package servicesample.business1;
2  import java.util.*;
3  import javax.swing.*;
4  import javax.swing.table.*;
5
6  public class Service1 {
7  /**入力情報0 関連定義 */
8  private String inProperty0 = "";
9  public String getInProperty0() {
10   return inProperty0;
11 }
12 public void setInProperty0(String inProperty0) {
13   this.inProperty0 = inProperty0;
14 }
15 /**入力情報1 関連定義 */
16 private String inProperty1 = "";
17 public String getInProperty1() {
18   return inProperty1;
19 }
20 public void setInProperty1(String inProperty1) {
21   this.inProperty1 = inProperty1;
22 }
23 /**
24 * JSP から呼び出されるメソッドです。当該メソッドで出力プロパティ情報を設定します。
25 * 業務ロジックが正常に終了した場合は true を返却します。
26 * 入力パラメタエラーの場合は false を返却します。
27 * データベースや EJB アクセスによりエラーが発生した場合は例外(Exception)をスローします。
28 */
29 public boolean service1Method() throws Exception {
30   boolean rc = true;
31   this.setOutProperty0(this.getInProperty0());
32   this.setOutProperty1(this.getInProperty1());
33   return rc;
34 }
35 /**出力情報0 関連定義 */
36 private String outProperty0 = "";
37 public String getOutProperty0() {
38   return outProperty0;
39 }
40 public void setOutProperty0(String outProperty0) {
41   this.outProperty0 = outProperty0;
42 }
43 /**出力情報1 関連定義 */
44 private String outProperty1 = "";
45 public String getOutProperty1() {
46   return outProperty1;
47 }
48 public void setOutProperty1(String outProperty1) {
49   this.outProperty1 = outProperty1;
50 }
51}

 標準的な画面遷移パターン
―画面制御部分をパターン化し、画面と業務ロジックとの分割をさらに促進―

 ここまで、最適なWebアプリケーションモデルを設定し、画面と業務ロジックを分離することを説明しました。 しかし、Webアプリケーションは、先に紹介した入力画面および出力画面だけの単純な形態にとどまりません。ここでは、標準的な画面遷移パターンを紹介するとともに、そのパターンで前述のWebアプリケーションモデルがどのように適用できるかを説明します。

図3 標準的な画面遷移パターン

 図3に標準的な画面遷移パターンを示しました。一般にWebアプリケーションでは、Login画面から始まり次にメニュー画面となります。このメニュー以降に続く画面遷移として、大きく4つのパターンを示しています。

 まず1つ目は、入力画面からデータを入力して出力画面が表示される形態です。この形態については前述のWebアプリケーションモデルそのものです。以降、Webアプリケーションモデルの適用を表現するにあたり、入力画面と出力画面の対を“サービス”という言葉で表現していくこととします。

 2つ目の画面遷移は、1つのサービスの出力画面が次のサービスの入力画面となる形態です。複数の画面にまたがってデータを入力していく場合にこの形態を使用します。

 3つ目の画面遷移は、サービスの入力画面がなく、まず出力画面が表示される形態で、さらに出力画面が再帰的に繰り返される形態です。ユーザー情報をもとにデータベースから該当するレコードを検索、固定数レコード分だけ表示し、出力画面中の“前へ”“後へ”のボタン押下で、当該レコードを表示する場合に使用します。

 4つ目の画面遷移は、サービスの動的な遷移です。図3の例では、まず入力画面のないサービスがあり、その出力画面が次のサービスの入力画面となっています。その入力画面からのデータをもとに業務ロジックで後続のサービスを決定する形態です。例えば、金額を入力させてその額によって以降の画面遷移を制御したり、また、ユーザーIDによって表示する画面を制御する場合に使用します。いろいろなWebアプリケーションの形態がありますが、おおよそ、上記4つのパターンに集約できると思われます。

 画面と業務ロジックの並行開発手順

 これまで、画面と業務ロジックの並行開発を目的として、最適なWeb アプリケーションモデル、そしてこのモデルを適用した標準的な画面遷移パターンを紹介しました。

 最適なWebアプリケーションモデルに従うことで、入出力画面および業務ロジックは、それぞれ入出力画面用JSPと、制御用JSPそして、JavaBeansに分離されることを紹介しました。特に業務ロジックは、JavaBeans中のメソッドに相当します。

 画面の入出力データの定義(画面と業務ロジックとのインターフェイス定義)、そして画面遷移が標準的な画面遷移パターンのどれに適合させるかを決めることで、制御用JSPの構造が明確になり、より画面部分と業務ロジック部分の分離が可能です。これをもとにしたWeb アプリケーション開発での画面と業務ロジックに関する並行開発手順を図4に示します。

 まず、標準的な画面遷移パターン(図3)に従って画面遷移設計を実施します。そこで、各入出力画面(サービス)単位でデータの入出力インターフェイス(画面と業務ロジックのインターフェイス)を決定していきます。画面遷移設計結果および各入出力画面(サービス)ごとのデータ入出力インターフェイス、そして図1で示したWebアプリケーションモデルをもとに、入出力画面JSP、制御用JSP、 JavaBeansを開発します。

 以降、画面部分については、入出力画面JSP を、そして業務ロジック部分についてはJavaBeansを対象にそれぞれ並行に修正、実行確認をしていくサイクルとなります。

図4 画面と業務ロジックの並行開発

 ここまで、最適なWebアプリケーションモデルおよび標準的な画面遷移パターンを説明してきましたが、たとえば日立製作所の「Cosminexus」の開発環境に含まれるJSPウィザードは、これらのWebアプリケーションモデルおよび画面遷移パターンをサポートしています。

 このウィザードでは、前述した画面遷移パターンに従って、入出力画面のインターフェイスを定義し、この定義に基づいて入出力画面のJSPおよび制御用JSP、そして入出力プロパティを保持するJavaBeansを自動生成します。画面開発では、自動生成された入出力画面のJSPをオーサリングツールを用いて画面レイアウトを整えます。一方、業務ロジック開発では、自動生成されたJavaBeansのメソッドに、入出力プロパティ情報をもとに業務ロジック(Enterprise JavaBeans呼び出しロジックなど)を追加していきます。この手順で画面と業務ロジックの並行開発が可能となります。

 このJSPウィザードの画面遷移定義では、入力画面、業務ロジック、出力画面を1つの構成要素(サービス)として定義していきます。1つのサービス中の出力画面を次の入力画面としてサービスをつなげていくことが可能です。入力画面なしで業務ロジック実行後に出力画面のみ出力する形態も可能で、さらに出力画面の情報を同一サービスでの入力画面情報として定義することで、再帰的に業務ロジックを呼び出すといった繰り返しの画面遷移が定義できます。

 また、固定的なメニュー画面ではなく、画面入力されたデータ値によってそれ以降のサービスへの遷移が制御可能です。もちろん、サービスに固定的なメニュー画面をつなげることも可能です。

 画面遷移設計の注意点

 ここで、画面遷移設計をする際の一般的な注意事項を説明します。

(1)フレーム分割について
 フレームの分割は、画面レイアウトの設計で大きな位置を占めます。しかし、フレーム分割の多用はWebサーバとの通信回数が多くなり、フレーム分割しない場合に比べて性能が劣化するので注意が必要です。さらに、フレーム分割によって、クライアントは複数のフレームを並行して操作可能となるので、クライアントから複数の処理が同時にサーバに対して要求されるため、例えば複数フレームにまたがるデータをサーバ側で保持する場合では、ブラウザに対応する1つのHttpSession中のデータをフレームに対応した複数スレッドでアクセスすることになるため、データを更新する際、synchronized キーワードで、HttpSessionを指定するなどのロックの配慮が必要となります。

 一方、ブラウザによっては、同時に表示できるフレーム数が決められているものもあり、多くのフレームを表示する場合には表示が段階的に行われるため、あたかもサーバ側の処理が遅いかのようにユーザーに印象づけてしまう恐れがあります。このようなことを考慮すると、フレーム分割については、例えば左側にメニュー画面、右上に情報入力画面、そして右下に情報出力画面といった3フレーム程度のレイアウトとするのが妥当でしょう。

(2)サブミットボタンの連打について
 Webブラウザに表示されるボタンを複数回押下することにより、サーバにリクエストが複数回送信される場合があります。この場合、データベースへのデータの二重登録や二重更新といった問題が発生します。この問題を回避するため、以下の対処方法がよく使われています。

  1. JavaScriptによるボタンの複数押下の回避
    JavaScriptを使用し、ボタンが選択された際、フラグをオンにして2回目以降のボタン選択で処理が実施されないようにします。しかし、ブラウザの種類やタイミングによっては、この方法で完全に回避できない場合があります。

  2. 画面遷移をサーバ側で制御する
    サーバ側で画面遷移を管理し、同一の画面への遷移を禁止するようにします。

  3. 複数回のサーバへのリクエストを想定した業務とする
    重複してサーバへリクエストが送信されることを想定して、例えばデータの二重登録については、後続のデータ登録について、単純にデータベースアクセスのエラー画面を出すのではなく、データベースからのエラー情報をもとに、すでに登録済みである旨の画面を出すような仕様にしておきます。

 J2EEをベースとしたWeb アプリケーションのフロントエンド(画面系)の開発方法については、一般に、サンマイクロシステムズから「Java 2 Platform, Enterprise Editionアプリケーション設計ガイド」(J2EE Blueprintsとも呼ばれます)としてまとめている中に登場するJava Pet Storeを例とした「MVCモデル」が有名です。MVCはModel、View、Controllerの略で、MVCモデルは、Webアプリケーションサーバシステムを、Model(EJB)、View(JSP)、Controller(Servlet)の組み合わせで構成する考え方です。

 今回は、特に「MVCモデル」を意識せずWeb アプリケーションのフロントエンド(画面系)の開発手法を紹介しましたが、ここで紹介したWebアプリケーションモデルにおいても入出力JSPが、Viewに相当し、制御JSPがController、そしてJavaBeans部分がModelに相当しています。
J2EEをベースにアプリケーションを開発する場合、プログラム構造はMVCモデルでなくてはいけないとまでいわれるようになりましたが、その目的は、プログラムの役割を明確化し再利用可能なコンポーネントが開発しやすくなること、そして画面開発と業務ロジックの並行開発を可能にすることであることを再認識していただければ幸いです。

連載内容
Webと企業システムを結ぶ実践的アーキテクチャ
  第1回 Webと企業システムをつなぐ現実的なアーキテクチャとは?
第2回 最適なフロントエンド開発手法
  第3回 XML連携における具体的な手法
  第4回 Webアプリケーションにおける帳票実現
  第5回 COBOL資産を積極活用する
  第6回 ワークフロー連携



連載記事一覧




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

注目のテーマ

Java Agile 記事ランキング

本日 月間