XPagesアプリ設計の基本型

XPagesアプリを作り始めるときに、アプリの作り方の型があるとうれしかったような気がする。

我流だけど、自分のXPagesアプリの作り方の流れみたいなものを紹介してみよう。

DB

最初にXPageアプリの入れ物になるDBファイルを作成することになる。 DBファイル名がそのままXPageアプリの名前になるので、短くてわかりやすい名前にしておこう。

大文字、小文字の違いはXPagesサーバが吸収してくれるが、全部小文字が基本。

業務で使うXPagesアプリならテンプレートにすべきだけど、設計中はNSFファイルのままにしておくほうが楽。テンプレートにしてしまうと、 テスト実行するときにいちいち実行可能なNSFファイルに設計を反映しないといけないので、ちょっと手間。

DBの設定

箱ができたら、DB(アプリケーション)のプロパティで必要な設定をしておく。

フォームの設計

フォームの設計

永続性を「メモリに保持」にしてるのは、JavaScriptで自作のクラスを使うため。自作のクラスを使うときには、ページ情報をオンメモリにしておかないとエラーになってしまう。 たぶん、Dominoのバグというか制限事項。

フォームの作成

XPagesの場合、データのかたまりを「文書」内の「フィールド」に格納する。 文書にどんなフィールドがあるかは、「フォーム」で定義する。

フォームとフィールドの名前

というわけで、まずはフォームの設計を行う。フォームの名前には、保存される文書をどう呼ぶのがしっくりくるかを基準に考えればいい。

例えば、人事情報を扱うXPagesアプリケーションでは、各社員毎に作成される文書を「社員文書」と呼ぶなら、そのフォーム名は「社員」にしておけばいい。 会社の情報を保存するフォームなら「会社」のように。

なお、XPagesのコードでは、フォーム名は文書を新規に作成するときくらいにしか使わないので、多少わかりにくい名前でも何とかなる。

各データ(氏名、所属、生年月日など)はフィールドに格納されるので、大事なのはフィールドの名前。

XPagesのコードでデータを読み出すときには、フィールド名を指定して読み出すので、格納されているデータが何なのか連想しやすいフィールド名にすることが必要。 日本でしか使わないアプリなら、フィールド名を漢字で表記するのもアリだと思う。

XPagesの場合、フィールドの配置は、XPagesの画面上での見栄えとは関係ないので、適当に配置していけばいい。 ただ、アプリケーションのデータ保守のために、Notesクライアントで文書を開くことはあるので、配置の工夫と注釈はちゃんとつけておく。

フォームの設計

フィールドの種類

フィールドにはいろんな種類があるが、XPagesで使うのは以下の6種類。

フィールドの種類がなんであっても、XPagesのコードによってどの形式で保存されるか決まるのだが、Notesクライアントで文書を変更したときに、この設定が効くので、けっこう大事。

テキストフィールドには32KBの壁があるので、大きめのJSONデータを格納するなら、最初からリッチテキストにしておこう。

フィールドの複数値設定

フィールドには複数値をとるかどうかの設定があるが、アプリの仕様で複数値をとるなら複数値設定をアリにしておけばいい。 設定がどうであっても、XPagesフィールドに値を書き込むときの処理によって複数値になるかどうかが決まるのだが、こちらもやはりNotesクライアントで文書を編集したときに、 この設定に応じてデータが格納されるので、大事な設定。

JSON形式のデータを格納するフィールドは複数値設定をはずしておくこと。Notesクライアントで保存しなおすと、JSONデータが崩壊する。

デフォルトアクセス用のフィールド

読者フィールドを用意して、アクセス制御するときに、設計ミスによって、変なユーザー名を入れてしまうと、Notesクライアントから開いても読めない文書ができてしまうことがある。 そのようなケースに備えて、動的にアクセス権を制御するフィールドとは別に、[デフォルトアクセス]みたいな名前の作成者フィールドを用意しておき、文書保存時にはそこに "[メンテ]"のようなロール名をセットするようにしておくといい。

Notesクライアントのユーザーをそのロールに組み込んでおけば、Notesクライアントからならいつでも開けるようになる。

まぁ、最終手段はDomino Administratorの「フルアクセスアドミニストレーション」モードを使ってアクセス権を再設定してやればいいわけだが。。

ビューの用意

ビューはXPagesで文書の一覧表示、目的の文書を検索、キーワードの抽出などいろんな用途に使う。これらはXPagesの設計をはじめたあとに必要に応じて作ればいいだろう。

あと、サンプルデータをNotesクライアントで入力したり、編集したりするためのビューは用意しておく。

XPagesの場合、一般ユーザーがNotesクライアントでアクセスしてくることはないので、"(検索用)"のようなカッコ付の隠しビューにする必要はない。

XPage

ブラウザで画面を開くのがXPageになるので、各画面の入口になる。

"XPages"は技術というか、プラットフォームの名前。XPagesアプリの各画面は単数形の"XPage"と記載する。

XPage

まずは名前。URLのこの部分になる。
フォームの設計

短くてURLに使って、画面の内容がある程度推測できるいい名前を付けよう。

XPageの設計

画面のレイアウトや機能を1つのXPagesに組み込むことは避ける。コードの見通しをよくするために、いくつかのパーツにわけて設計していく。

画面のパーツは「カスタムコントロール」で定義するので、各画面のトップであるXPageでは、こういうことをやる。

アプリの種類にもよるだろうけど、こんなレイアウトが基本になると思う。
フォームの設計

XPageは基本、こんな記述になる。

  01: <?xml version="1.0" encoding="UTF-8"?>
  02: <xp:view xmlns:xp="http://www.ibm.com/xsp/core"
  03:     xmlns:xc="http://www.ibm.com/xsp/custom">
  04:     <xp:this.resources>
  05:         <xp:script src="/common.jss" clientSide="false"></xp:script>
  06:     </xp:this.resources>
  07:     <xc:xcLayout>
  08:         <xp:this.facets>
  09:             <xc:xcForm xp:key="facetBody" />
  10:         </xp:this.facets>
  11:     </xc:xcLayout>
  12: </xp:view>
  

レイアウトを決める<xc:xcLayout>

全頁のレイアウトを統一的に決めているのは<xc:xcLayout>というカスタムコントロール。

  01: <?xml version="1.0" encoding="UTF-8"?>
  02: <xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xc="http://www.ibm.com/xsp/custom">
  03:     <div class="lotusFrame">
  04:         <xc:xcBanner></xc:xcBanner>
  05:         <xc:xcTitleBar></xc:xcTitleBar>
  06:         <div class="lotusMain">
  07:             <xc:xcMenu></xc:xcMenu>
  08:             <div class="lotusContent">
  09:                 <xp:callback facetName="facetBody" id="facetBody"></xp:callback>
  10:             </div>
  11:         </div>
  12:         <xc:xcFooter></xc:xcFooter>
  13:     </div>
  14: </xp:view>
  

カスタムコントロール

カスタムコントロールはメニューやヘッダだけでなく、メインコンテンツの内容を決めていく。

そこそこ複雑な構成の画面だと、各パーツごとにカスタムコントロールにしておくと、見通しがよくなる。 そのとき、同じメインコンテンツのカスタムコントロールがカスタムコントロールの一覧に集まって表示されるように、ファイル名をあわせておくといいと思う。

例えば、ある画面のXPageが"board.xsp"だとすると、そこで<xc:xcLayout>に組み込むメインのカスタムコントロールを"boardCC.xsp"、その中の各パーツをこんな感じで名前付けしていくといい。

などなど。

これを各XPageごとに用意しておけば、カスタムコントロールの一覧を開いたときに、目的のカスタムコントロールを探しやすくなる。

あと、画面によらず、共通に使えるカスタムコントロールは"ccManagement"みたいに、"cc"+機能名みたいに統一しておくといい。

SSJS(ServerSide JavaScript)

ちょっと大き目のXPagesアプリケーションの開発となると、いろんな処理を行うSSJSを用意する必要がある。 たいてい出てくる処理としては、

他にも、何かの情報の一覧を扱うクラスのようなものも出てくる。

SSJSのスクリプトライブラリは以下のように分類できる。

ページに関する処理を集めたライブラリには、そのページのXPage名と同じファイル名を付けておくとわかりやすい。 例えば、board.xspというページに関するライブラリは、board.jssという名前にしておく。

ページライブラリから呼ばれる共通の処理は、今回のアプリ特有の処理と、デバッグ関係などアプリによらず汎用的な処理がある。 アプリ特有の処理は、あまり使い回しができないような処理を集めておくといい。ファイル名はappCommon.jssなど。

アプリによらず汎用的に使えるライブラリはcommon.jssのようなファイル名を付けて、便利な処理を集めておくと、いろんなアプリで使い回しができていい。 あと、ユーザーの漢字名、所属、メールアドレスなど、アカウントに関する処理も使い回しができるが、アカウント関係の処理は結構多岐にわたるので、 account.jssのように別ファイルに分けておいたほうがいいだろう。

あとは、情報リストを扱うライブラリだが、クラス名をそのままファイル名にするといい。例えば、部品のリストを扱うクラス名がPartsListだったらPartsList.jssにする。

余談だが、機能別にライブラリを用意するという方法もあると思うが、たぶんやめておいたほうがいい。例えば、文書を開く時の処理や保存関係の処理を集めると、ページごとに似て非なる関数が 集まってくるので、けっこうごちゃごちゃしてくる。また、将来、別のアプリに機能別ライブラリを移植しようとしても、いろんなページの処理がごった混ぜになっているので、手直しが多くなる。 実際やってみたが、ページごとにまとめる方が無難だと思う。

ページ特有のライブラリの書き方

ページ特有のライブラリは、XPageやカスタムコントロールから呼び出される公開用の関数(メソッド)と、ライブラリ内だけで使われる関数がある。前者を「パブリックメソッド」、 後者を「プライベートメソッド」と呼ぶ。

ライブラリ内に、フラットにメソッドを定義すると、そのメソッドがパブリックなものか、プライベートなものかは、一見するとよくわからないので、メソッド名の先頭を'_'で始めると、 プライベートメソッドであるというルールを付けることになる。

  // xxxライブラリ
  function method_A() {
  }
  
  function method_B() {
  }
  
  // ここから下はプライベートメソッド
  function _private_A() {
  }
  
  function _private_B() {
  }
  

ただ、できればパブリックメソッドとプライベートメソッドが機構的に分離できたほうがいい。

JavaScriptでパブリックメソッド、プライベートメソッドっぽい記述をする方法を探すと、以下のような方法があるらしい。 参考にしたのは、「JavaScriptの名前空間定義チートシート」。

こういう書き方ができる。

  $common = {};
  (function() {
    // Public Methods
    $common.method_A = function(params) {
      _priv_func_A(params);
    }
    $common.method_B = function(params) {
      _priv_func_B(params);
    }
  
    // Private Methods
    function _priv_func_A(params) {
      //...
    }
    function _priv_func_B(params) {
      //...
    }
  })();
  

この方法だと、既存のライブラリをこの方式に変えるのは簡単。 ページの処理をまとめたライブラリであれば、$commonの部分の名前はページ名とあわせておけばいい。

ページ関係のライブラリの構成

ページ関係のライブラリで必ず出てくるのが、こんな処理。

保存時の処理として、サブ的な処理としては、こんな処理もある。

これらをページ関係のライブラリに組み込んでいく。

ページ関係のライブラリのメソッド名の例

どんなページでも出てくる処理に、同じメソッド名を付けておくと、統一感が出てイケてる感じになるし、メソッド名を見ただけで何をやろうとしてるのか 把握できるようになる。もちろん、処理の移植も簡単になる。

私の場合、ページ関係のライブラリでは、こんな感じのメソッドを用意してる。 人物情報表示のページの名前がperson.xspだとすると、こんな感じになる。もちろん、各メソッドの中身はページごとに異なる。

  // 人物表示ページライブラリ
  $person = {};
  (function() {
    // Public Methods
  
    // 文書Open時の処理
    $person.postOpenDoc = function(xsp_doc) {
    }
    // 文書新規作成時の処理
    $person.postNewDoc = function(xsp_doc) {
    }
    // 文書保存時の処理
    $person.saveDoc = function(xsp_doc) {
    }
    // xxxの表示判定
    $person.isDispXXX = function(xsp_doc) {
    }
    // ---- Private Methods -----
    // アクセス権の設定
    function _setAccessPermission(xsp_doc) {
    }
    // 文書のステータスの更新
    function _updateDocStatus(xsp_doc) {
    }
    // 通知メールの送信
    function _sendMail(xsp_doc, mailAddList) {
    }
    // 変更履歴の保存
    function _recordModifyHistory(xsp_doc) {
    }
  })();
  

一応、この仕組みの説明を。

XPageやカスタムコントロール内からのページ内ライブラリの呼び出し

XPageやカスタムコントロール内から、ページライブラリのメソッドを呼び出す方法を説明する。

以下のコードはXPageのコード。これはページのbeforePageLoadイベントの処理になる。まぁページを開く前に呼ばれる処理と思えばいい。 これで$form.postOpenDoc()が呼ばれる。

  <xp:view xmlns:xp="http://www.ibm.com/xsp/core">
    <xp:this.beforePageLoad>
        <![CDATA[#{javascript:$form.postOpenDoc(document1);}]]>
    </xp:this.beforePageLoad>
    <xp:this.data>
        <xp:dominoDocument var="document1" formName="main"></xp:dominoDocument>
    </xp:this.data>
  

スクリプトはこんな感じの処理を書く。

  $form.postOpenDoc = function(xsp_doc) {
      $common.debug("$form.postOpenDoc() is called.");
      if (xsp_doc.isNewNote()) {
          var now = @Now();
          var dateStamp = [@Year(now), @Month(now), @Day(now)].join('/');
          xsp_doc.setValue("作成日", dateStamp);
      } else {
          var json_text = xsp_doc.getItemValueString("リスト情報");
          viewScope.myList = fromjson(json_text);
      }
  }
  

アカウントライブラリの紹介

ユーザーアカウントのいろんなサービスを提供してくれるライブラリの紹介。

ちょっと量が多めになるので、「アカウントライブラリ」を参照。