S2Clickアプリケーションの設計方針

S2Clickではアプリケーションを以下のコンポーネントの組み合わせで作成します。 なお、ここではデータベースアクセスにS2JDBCを使用するものと仮定しています。 S2JDBC以外のデータベースアクセスフレームワークを使用する場合はサービスの部分を読み替えてください。

また、本ドキュメントはClickの基本的な知識を有しているものという前提に基づいています。 Clickに関する基礎知識についてはClick Wikiなどを参照してください。

コンポーネント 命名規則 説明
ページ ルートパッケージ.page.XxxPage 画面に対応するクラス。初期表示データの取得、ボタンクリックなどのイベント発生時の処理などを行う。 S2ClickPageクラスを継承して実装する。
フォーム ルートパッケージ.form.XxxForm 入力値を格納するクラス。フォームの再利用を行わない場合はページクラス内のインナークラスとして作成してもよい。 S2ClickFormを継承して実装する。
サービス ルートパッケージ.service.XxxService S2JDBCのJdbcManagerを使用してデータベースアクセスを行うクラス。 小規模なアプリケーションではページクラス内で直接JdbcManagerを使ってもよい。

サンプルを通して各コンポーネントの実装方法を紹介します。 このサンプルは入力フォームから投稿したメッセージをデータベースに登録し、 新着順に一覧表示するという簡単なメッセージボードアプリケーションをイメージしています。 また、Clickに関する基本的な知識はすでに持っている前提で説明を行います。

フォームクラス

まずはフォームクラスです。 Clickのサンプルではページクラス内でフォームを組み立てる手法が用いられていますが、 そうするとページクラスがフォームを組み立てるためのコードで埋め尽くされてしまうこと、 また登録画面と編集画面など、類似のフォームの共通化が難しくなってしまうことなどから S2Clickではフォームを単独のクラスとして作成することを推奨しています。

フォームクラスはorg.seasar.s2click.control.S2ClickFormを継承して実装します。 setFieldAutoRegisteration()メソッドにtrueを設定しておくと、 フィールドとして定義されたコントロールが自動的に自分自身に追加されます。

package org.seasar.s2click.example.form;

import org.apache.click.control.Submit;
import org.apache.click.control.TextField;
import org.seasar.s2click.control.S2ClickForm;

public class MessageForm extends S2ClickForm {

  private static final long serialVersionUID = 1L;

  public TextField name = new TextField("name", true);
  public TextField message = new TextField("message", true);
  public Submit submit = new Submit("add");

  public MessageForm(String name){
    super(name);
    setFieldAutoRegisteration(true);
    setJavaScriptValidation(true);
  }

} 

ページクラス

続いてページクラスです。 フォームを組み立てるためのコードがフォームクラスに分離された以外は通常のClickのページクラスとそれほど変わりはありません。 フォームクラスはnewして使用し、データベースにアクセスするためのサービスクラスをDIしています。

ページクラスはorg.seasar.s2click.S2ClickPageを継承して実装します。 この例では使用していませんが、S2ClickPageを継承することでバイナリやJSONをレスポンスに書き出すためのメソッドや、 @Requestアノテーションによるリクエストパラメータのフィールドへのバインド機能などを利用することができます。

package org.seasar.s2click.example.page;

import java.util.Date;

import javax.annotation.Resource;

import org.seasar.s2click.example.entity.Message;
import org.seasar.s2click.example.form.MessageForm;
import org.seasar.s2click.example.service.MessageService;

public class MessagePage extends S2ClickPage {

    @Resource
    protected MessageService messageService;

    public MessageForm form = new MessageForm("form");

    public MessagePage(){
        form.submit.setListener(this, "doAdd");
    }

    @Override
    public void onRender() {
        addModel("messageList", messageService.getMessages());
    }

    public boolean doAdd(){
        if(form.isValid()){
            Message message = new Message();
            message.name = form.name.getValue();
            message.message = form.message.getValue();
            message.date = new Date();
            messageService.insert(message);

            setRedirect(MessagePage.class);
            return false;
        }
        return true;
    }

} 

サービスクラス

最後にサービスクラスです。 ここでは実装を単純にするためS2JDBCが提供するorg.seasar.extension.jdbc.service.S2AbstractServiceを継承していますが、特にその必要はありません。 実際のところ、エンティティとサービスを一対一で作成するケース以外ではS2AbstractServiceは使いづらいでしょう。

小さなアプリケーションではサービスレイヤを設けず、ページクラスに直接JdbcManagerをDIしてデータベースアクセスコードをページクラス内に記述するという選択も考えられますが、 ページクラスにデータベースアクセスコードを含めてしまうとユニットテストが難しくなるというトレードオフがあります。

package org.seasar.s2click.example.service;

import java.util.List;

import org.seasar.extension.jdbc.service.S2AbstractService;
import org.seasar.s2click.example.entity.Message;

public class MessageService extends S2AbstractService<Message> {

    public List<Message> getMessages(){
        return jdbcManager.from(Message.class).orderBy("messageId desc").getResultList();
    }

}