[続開発プロセス#13] INTER-Mediatorによるアプリケーション

制約のある状況での開発プロセスについて、MVCフレームワーク、FileMakerでそれぞれ簡単ですが、設計をやってみました。そして、本題であるINTER-Mediatorでの設計を今回は説明します。

まず、以下の図は、共通の設計モデルを示すクラス図に、INTER-Mediatorで考慮すべきことをメモとして追加したものです。まず、CategoryModelに紫色のメモで示したように、カテゴリーの全リストはINTER-Mediatorでは不要なので、ここメソッドを削除しています。また、PickingUpクラスやMemoListクラスの属性は、結果的にデータベースのテーブルにあるフィールドが必要になるので、ここではどんなデータがUIのレイヤーに来るのかを明示するため、属性にフィールド名を追加しました。

まず、前の図の下の方にあるステレオタイプがtableの部分をみてください。これは、ドメインモデリングの作業で作ったものと同じであり、もちろん、このままデータベースのスキーマとして定義します。アクセス権の設定なども行、データベースの準備は進めておきます。そして、ステレオタイプがmodelのものは、「データベースから何が抽出されたいか」を操作で示しています。これらの機能を、この後の定義ファイルの設計において、コンテキストとして取り出せるものを用意する段階で改めて調べます。

そして、前の図の上半分に記述されたUIレイヤーについて検討を進めます。まず、UI Componentステレオタイプの2種類のものは、INTER-Mediatorでは実装された機能やプラグインとして用意されているので、それらを呼び出すだけでOKです。ここは機能そのものやプラグインの制限が影響なければ特段の配慮は通常は不要です。

前の図で、緑色に塗ったクラスは、結果的にはある段階で、エンクロージャーとして展開可能なものと対応づけるようにします。INTER-Mediatorでの設計のポイントは、エンクロージャーとリピーターの特定、つまり、どこでこれらの要素を出すのかによります。ここでは設計段階なので、まずは、データベースから得られる1つのリレーションに対して、UIレイヤーでは1つのクラスとして認識するところまでを進めます。つまり、メモの一覧がデータベースにアクセスしてリレーションとして得られます。リレーションなので複数のレコードですが、そのレコードには、緑色のクラスの属性にあるようなフィールドが存在するはずです。その意味で、緑色のクラスがリレーションかあるいはエンクロージャーを示すのであれば、その属性というのは途中をすっ飛ばした不正確な記述になるかと思いますが、ここでは勘弁性を優先してこのように記述します。この記述については、定義ファイルの設計でも同様な考慮をします。

MemoListクラスは、データベースから得られたメモのリストを展開するという意味ではわかりやすいでしょうけども、PickingUpは、大分類と小分類が入り乱れます。一応、1つのクラスPickingUpとして記述しましたが、それは正しいでしょうか? ですが、まずはカテゴリを取り出してボタンと一緒に表示するという仕組みが必要であることを表明するためとしてPickingUpクラスの存在は無駄なものではありません。しかしながら、大分類/小分類というかなりややこしい状況があります。

ここから、HTMLのテンプレートを実際に作ります。もちろん、これはINTER-Mediator用語で言えばページファイルを作成に入ります。ここで解決すべきことは、エンクロージャーとリピーターの特定になります。もちろん、要素の取り出しも重要ですが、INTER-Mediatorではこの「エンクロージャーとリピーターの特定」さえできれば、モデリングは終わったも同然だと考えられます。ただ、今回は設計プロセスを追うのが趣旨なので、詳細に記述します。

エンクロージャーとリピーターの特定に必要なことは、得られるリレーション間の関連です、つまり、リレーションシップの特定に他なりません。まず、前のシステムデザインで記述されたMemoListについては、メモが複数あるので、リスト全体とメモ単体の間に1対多の関係があり、ここで1に対してエンクロージャー、多に対してリピータを割り当てれば良いので、ここでのエンクロージャーとリピーターの識別は容易です。なお、結果的に1:*になる部分は*の数が一定しない、つまりデータに応じて変わる部分であり、結果的にデータベースからのリレーションを展開することになるでしょう。ボタンが10個並ぶなら、1:10になるので、これらはスタティックなものとも言えます。つまり、HTMLのテンプレートをモデリングした場合に出てきた1:*の部分がエンクロージャーとリピーターとして記述すべき箇所になるということが言えます。

しかしながら、ここで、ステレオタイプがtableの部分をみてください。memoテーブルのtop_category_id、sub_category_idは、それぞれcategoryテーブルのcategory_idと対応づけていて、ここで各メモがどのカテゴリーに分類されているのかを記録できるようになっています。また、category同士のリレーションシップも設定されており、これにより、大分類と小分類のレコードの関連づけが行われています。このようなスキーマにみられるリレーションシップは、エンクロージャーとリピーターの内部にさらにエンクロージャーとリピーターが存在することを示唆します。そのことを考慮して、ページファイルの中心部分をモデル化したものは以下の通りです。

まず、HTMLテンプレートの設計の上部を説明しましょう。ここは、システム設計でのPickingUpの部分です。ここでは、大分類と小分類が一覧されているという見え方になっていますが、もう少し詳細に見ると、1つの大分類に対するその小分類が複数リストになっています。つまり、大分類と小分類が1対多の関係になっているものが、さらに、大分類の数だけ並んでいるということになり、全体から見れば、多数の大分類があり、さらに、その大分類に対応した多数の小分類があるという階層構造になっています。これらは、ネストしたエンクロージャー/リピーターで定義します。外側のエンクロージャー/リピーターは次のように定義するとします。class属性は、HTMLテンプレートでのクラス名をつけておきます。

<div data-im-control="enclosure" class="CategoryBox">
    <div data-im-control="repeater" class="MajorCategoryBox">
        <div data-im="****@category_name"></div>
    </div>
</div>

このリピーターは、大分類の項目1つに対応します。なので、リピーターの中でcategory_nameフィールドを表示すると、このエンクロージャーに割り当てるコンテキストが大分類の一覧にしておくことで、ページ上には大分類だけの一覧が表示されます。コンテキスト名はまだ決めていないので、****にしておきます。

そして、このリピーターの中に、対応する小分類の一覧を表示するエンクロージャー/リピーターを定義します。次のようになります。

<div data-im-control="enclosure" class="CategoryBox">
    <div data-im-control="repeater" class="MajorCategoryBox">
        <div data-im="****@category_name"></div>
        <div data-im-control="enclosure" class="MinorCategoryContainer">
            <div data-im-control="repeater" class="MinorCategoryBox">
                <button onclick="****">選択</button>
                <div data-im="****@category_name"></div>
            </div>
        </div>
    </div>
</div>

ここで、内側のエンクロージャー/リピーターの表示を行うためにリレーションの取得を行う場合、その時の外側のリピーターで展開している現在のレコードと照合する、つまりリレーションシップを考慮することが、定義ファイルでの設定で可能です。その仕組みを利用して、内側のエンクロージャー/リピーターでは特定の大分類に対する小分類だけが表示されるようになります。

引き続いてMemoLineのリピーターから続く2つのエンクロージャーについて説明します。ここではまず、memoテーブルだけだと、大分類や小分類は主キー値を記録していることで、どの分類なのかは記録されていますが、単なる数字なので、人間が目で見て判別するのは困難ということがあります。つまり、memoテーブルの値に加えて、選択した分類の名前を知りたい分けです。名前が別テーブルにあるので、つまりはそこでリレーションシップに応じたテーブル連結をするのがSQLでの定番ですが、UIレイヤーではそういう用途にselectタグによりポップアップメニューが使われてます。ポップアップメニューは、ここでは、top_category_id値を対応させることができますが、そのcategory_idに対応する値を内部で記録しつつ、ページ上には別の文字列を表示することもできます。この場合、categoryとmemoが1対多の関係になりますが、逆にメモの一覧にある大分類のポップアップメニューには、選択された大分類項目だけでなく、選択肢として表示されるように全ての大分類の項目が必要であり、それぞれoptionタグでselectタグの内部に記述しなければなりません。つまり、selectは選択肢の提供と、選択したものの名称表記の両方の機能を持っているのです。逆に言えば、selectを適切に使えば、選択肢の用意だけで選択したものの名前はそこにあるものを使うので、一石二鳥であるとも言えます。INTER-Mediatorでは、selectとoptionの関係をエンクロージャー/リピーターとして扱うので、その関係をモデル図の右下のあたりに2箇所記述しました。

INTER-Mediatorで一番難しいのは、このエンクロージャー/リピーターの特定、そして、その階層関係での定義ではないかと思われます。エンクロージャー/リピーターを特定するには、そこにリレーションが得られるという事実と関連づければ良いのですが、階層関係になると非常に難しくなるというのはあるでしょう。やはり、動作原理を知るのがしっかりとした設計を行うためには必要なことは言うまでもありません。

ここまでのところで5つのエンクロージャー/リピーターが出てきました。よって5つのコンテキスト定義が必要という見方もできます。ここで、コンテキストを実際に定義して行く作業に入ります。実際には4つの定義で問題ないことがわかります。

ここで、コンテキスト定義は、以下の図のように、Contextクラスを元にインスタンス化したオブジェクトとして記述することにします。現実には連想配列なのでコンテキスト定義は言語の意味でのクラスを元にはしていませんが、認識可能なキーが決まっているのは、属性が定義されていることと同義ですので、この点は問題ないと思われます。実際には、コンテキスト定義を利用して、コンテキストオブジェクトが生成されます。正確に記載すると以下の図の左側、Precise wayと書かれた方のクラス図になります。つまり、コンテキストオブジェクトConcreteContextObjectはテーブルConcreteTableを継承しているか、あるいは参照することになり、その結果、ConcreteEntityの配列がstore属性から参照できるようになっています。ここで、ConcreteContextObjectをインスタンス化した時、当然ながら継承元のフィールドがそのまま所有します。ここで、テンプレートバインディングやあれこれとUMLの仕組みを試したのですが、モデルとしてしっくりくるものがありません。そこで、右側のように、Concise wayと書かれたように、コンテキストで利用する実テーブルであるTableを継承して、ContextObjectを定義します。名前は任意にして、このような目的のクラスであることをステレオタイプのcontext objectで示すことにします。こうすれば、右下のようなオブジェクトが記述でき、テーブルのフィールドをそのまま持ちます。もちろん、この表現は不正確であり、コンテキストオブジェクトはTableによるレコードの配列を持つのではありますが、そこを正確に全部記述すると図が煩雑になるだけなので、「実際にはそういうことだけども勘弁性を重視してこのように書く」という合意ができたとして、Concise wayのような表記をします。

ここでHTMLテンプレートの設計を見ながら、コンテキスト定義のオブジェクトを検討します。ここでは代表的な属性だけを記述することにします。結果をまとめると以下の図になります。

まず、カテゴリの一覧部分、つまりHTMLテンプレートの設計でいえば、CategoryBoxクラスとMinorCategoryContainerクラスを取り出します。これらはエンクロージャーなので、リレーションと対応させないといけません。ここでは図で示せるように、tableステレオタイプのcategoryクラスを継承して、top_category_listとsub_category_listのコンテキストオブジェクトクラスを定義し、それらのインスタンスとして、緑色のボックスのオブジェクトを記述しましたが、これは実際にここまでの記述は不要でしょう。記述すべきは山吹色のボックスのコンテキスト定義のオブジェクトのみです。コンテキスト定義のtop_category_listでは、viewあるいはtable属性を見ることで、リレーションの元になっているテーブルが判別できるので、結果的に薄黄色と緑色のボックスは、コンテキスト定義の情報や、スキーマの情報からすぐに入手可能であるということです。

同様にして、memo_list、minor_category_list、major_category_listのコンテキストを定義します。memoテーブル由来のコンテキストはmemo_listのみですが、残りの4つのコンテキストはcategoryテーブルを由来としています。このように、同一テーブルからであっても場面によって(つまりコンテキストによって)必要とするデータは異なり、その結果定義は別々に必要ということになります。ここで、major_category_listとyop_category_listの定義は同一なので、これは一方だけを使うことでも構いません。もちろん、名前だけが違う同一の定義が2つあっても構いません。そこは好みの問題です。定義をよく見ると、parent_category_idがnullのレコードが大分類の項目であり、このリレーションはコンテキストには強く依存せず、どこでも同じ条件で取り出しができることになります。minor_category_listとsub_category_listはどうでしょうか? relationshipの定義が微妙に違います。カテゴリのリストの方は、大分類そのものと関連があるのに対して、メモの方はメモで選択されている大分類項目と関連があります。つまり、これら2つのコンテキストは、別々のものです。つまり、場面が違うものであり別々に定義しなければならないことになります。

ここまで設計すれば、あとは手を動かして実装するだけになります。必要な属性などを追加してページファイルを作り、コンテキスト定義をまとめて定義ファイルを作ります。

ここで、HTMLテンプレートのAllSelectButtonやMinorCategoryButtonを見てください。ここで、ボタンを押して、メモの一覧に検索をかけて絞り込んだり解除したりということが必要になっていますが、INTER-Mediatorの機能ではそれは含まれていません。そういう仕組みのボタンは提供されていないのですなので、ここはどうしてもJavaScriptで実装をしなければなりません。それぞれのボタンは次のように定義しましょう。

<button onclick="redrawMemoList()"
        class="AllSelectButton">全選択</button>
    :
<button onclick="redrawMemoList($)"
        class="MinorCategoryButton"
        data-im="sub_category_list@category_id@$onclick">選択</button>
<div data-im="sub_category_list@category_name"></div>

対応するプログラムは例えば次のように作成します。つまり、memo_listコンテキストに対する検索条件を、状況に応じて切り替えれば良いということです。

function redrawMemoList(id) {
  let idValue = parseInt(id)
  INTERMediator.clearCondition('memo_list')
  if(idValue) {
    INTERMediator.addCondition('memo_list',
        {field:'sub_category_id',operator:'=', value:idValue)
  }
  const context = IMLibContextPool.contextFromName('memo_list')
  INTERMediator.constructMain(context)
}

以上がINTER-Mediatorでの実装設計になりますが、実質的には、ページファイルや定義ファイルがモデルそのものであるとも言えます。一度実装に近いクラス図などを記述するのも良いのですが、結果的にHTMLや定義ファイルを書いてしまった方が早いとも言えます。つまり、デザイン時の作成物と、目的とする作成物が近いというのがこうしたツール(いわゆるローコードツール)の特徴なのではないかと思われます。

そして、INTER-Mediatorでは、エンクロージャー/リピーターの理解は不可欠です。このようなツールごとに理解しておかないといけない特有の概念は必ずあり、それが最も注目すべき制約ということになるのではないかと思われます。

[続開発プロセス#12] FileMakerによるアプリケーション

ここまでに設計を進めてきたアプリケーションの設計図は以下の通りですが、途中でモックアップを作るときにHTMLを使ったので、それをいきなりFileMakerに切り替えるのは若干無理があると思われるかもしません。しかしながら、モックアップを作る意義は、何をしたいのかを明確にすることであり、ここまでの設計で必要な仕組みが概ね抽出できているということが重要です。クラス図にあるPickingUpとMemoListを見ることで、必要な仕組みを思い出せるはずです。ですが、やはりFileMakerは大きく違う環境です。実際にFileMakerで開発をする方々は早い段階で、FileMakerでどうすればうまく実現できるか、言い換えればFileMakerにおける制約を考慮し実現可能な設計を模索するはずです。もちろん、最初からやればいいのではと思われるところですが、以前にも書いたように、環境を固定してしまうことで、本来やりたい要求を制限し、視野の狭い発想になりがちであるため、まずは必要な仕組みや機能を抽出した上で、どのように実装環境に適応するのかを考えるべきです。

FileMakerで実装する場合、以下の設計図だと、データベースはもちろん問題なく実現可能なところですが、レイアウトに2つのTOからの展開をする方法を検討する必要があります。FileMakerではウインドウ全体を占めるレイアウトには、1つのTOしか設定できません。そのため、同一ウインドウ内に2つの「リスト」を展開するには、それぞれをポータルで展開するか、設計をもう少し変更する必要があります。今回のアプリケーションではポータルで展開する方法でもできそうですが、実際にはポータルでは制約が多くあることから避けたいと考えます。後からの設計変更に対応できない場合が出そうな気がするからです。そこで、今回は、メモのリストをレイアウトに割り当てることにします。そうなると、カテゴリの選択は、別のウインドウに出すとか、別のレイアウトにして切り替えるなどのアイデアが思いつきますが、ここではメモのリストのヘッダ部分で、分類をポップアップメニューで選択して、その選択結果を検索条件としてメモのリストを絞り込むと言うことにしました。カテゴリの一覧は制限がありますが、ポップアップメニューであればそれに近いと言うことで、このような実装にします。また、FileMakerの場合、ページネーションを入れる必要は薄いと思われるのでページネーションは不要とします。日付時刻の入力は、FileMakerの機能が利用できるので、コンポーネント群の利用は不要と考えます。こうした決定をまずはメモとして書き込んでおきました。

FileMakerでの一番大きな制約は、開発言語で使われるようなスタイルのクラス定義ができないことです。そして、レイアウトをベースに画面を構築することになりますが、リレーションシップでのTOや値一覧など、FileMaker特有のオブジェクトがあり、それらを適切に設計する必要があります。

ここでは1つのレイアウトにメモを一覧することを基調としているので、メモの内容をボディパートに配置し、リスト表示にして一覧できるようにすることが基本です。以下の図はレイアウトの設計をしたところです。LayoutからBodyPart以下の内容は特に説明は不要でしょう。これらは、MemoのTOとバインドすることで、表示と編集が可能になります。自動化されるところはこれ以上は記述する必要はないでしょう。ここで、メモの中にある大分類のポップアップメニュー(MajorCategorySelect)と、小分類のポップアップメニュー(MinorCategorySelect)です。ポップアップメニューの設定結果はフィールドに保存されるのでMemoと言うTOと結びつきますが、一方、選択肢を構築しなければなりません。そのために値一覧(ステレオタイプがValue List)をそれぞれアサインする必要があります。大小どちらの分類も、categoryテーブルを元にしたTOを割り当てますが、大分類はもちろん小分類を省いたものですし、小分類は現在選択されている大分類に応じた選択肢が出ている必要があります。ただし、レイアウトを検討している段階では、そこのTOにどんなデータが出て欲しいかと言うことをまずは明確にすることが必要です。よってTOとValu List、そしてコメントだけを記載しました。ヘッダパートはボタンが2つとポップアップメニューが2つです。このポップアップメニューは、メモの検索条件を与えるためのものであり、Memoの1レコードとは独立しています。従って、Value ListやTOも別途与える必要があります。なお、実際には何かフィールドをアサインしないとポップアップメニューは構築できないので、こちらはグローバルフィールドを定義することにします。

レイアウトとしてのモデルが作成できれば、それとデータベースを合体させて、さらに必要な定義を検討します。以下の図はこれまでの作業を合わせたものですが、クラス図で記述する場合、テーブルとTOはスーパークラス/サブクラスの関係で記述するのが良いでしょう。そのデータベースですが、いくつかフィールドを追加しています。memoテーブルのalways1は文字通り、計算結果で常に1を値として持ちます。また、categoryテーブルでは大分類なら1を返す計算フィールドを定義しておきます。他にmemoテーブルではヘッダ部のポップアップの値を受けるためのグローバルフィールドを定義しました。ここで、TO同士のリレーションシップも線で結んでおき、その時の条件をメモで記述しました。大分類は、is_parentが1のものに絞り込む必要がありますし、小分類は大分類で選択されている項目の値とparent_idが同じものに絞り込むことで現在の大分類に応じた小分類の一覧が表示されます。TOのMemoはレイアウトに割り当てられ、レイアウト上のオブジェクトとバインドされるので、それらも線を引いて表現をしました。

ここまでくれば、あとは実装です。ただ、本当にこれでいいのかと思うところもあるかと思いますので、完全ではありませんが、作ったデータベースをダウンロードできるようにしておきます。

今回のアプリケーションだと、メモの側に見える分類は、ポップアップメニューによって、項目名が見えているので、テーブル同士のmemoとcategory間に参照関係は、TOの中には明確には見えていません。値一覧を通じて間接的な関係しかありません。ER図での関係と、TOの関係すなわちFileMakerデータベースのリレーションシップは、微妙に違うと言うか、視点の違いが読み取れます。言い換えれば、FileMakerでは値一覧をリレーションシップを用いて定義をしているのですが、汎用的な設計には値一覧のような存在は顕在化していないと言うことにもなります。

実際、FileMakerで、このようなモデルベースの開発をするかと言うと、おそらくほとんどの方はやっていないとおっしゃるでしょう。最後の図のような関係は、FileMakerの様々な画面で確認は可能ですが、それらの画面で定義した様々なオブジェクトの間の関係は、頭の中で統合しているのではないでしょうか。そうして実際の開発できる方、あるいは迷いながら開発する方も含めて、FileMakerでは「試行錯誤しながら完成に近く」と言うことが可能です。結果的にこのような設計を示しましたが、ざっくりと設計してあとは動きを見ながら調整すると言うことになるでしょう。これは、FileMakerが汎用言語による開発との違いでよく語られる点ではありますが、それが可能なのは、FileMaker自体、設定できるものに制約が大きくあり、自由度が低いことから、限られた要素のコントロールしかできず、それによって逆に見通しがよくなると言うことがあるのです。もちろん、限られたコントロールでもアプリケーションが作れるほどの仕組みがあるから実用に耐えられるのです。

汎用言語で散々作ったものが設計が悪いとなって修正するのは非常に骨が折れる仕事であることはよく言われていますが、FileMakerではどうでしょうか? 実は複雑なスキーマ、大量のスクリプトによる処理が絡んでくると、FileMakerでも見通しは悪くなります。汎用言語での開発だと、設計をあらかじめしておくことで問題点を先に洗い出すことが行われます。一方、FileMakerの場合はあまりこうした設計を重視せず、むしろ試行錯誤ができる点を頼りに進めることはよくありますが、結果としてある程度作ったあたりから特定の機能が得られなかったり、ちょっとした改変が難しかったりと言うことにもなりがちです。FileMakerだからと言って設計をきちんと行わないと、汎用言語で開発する場合と同じようなことは起こると言えるでしょう。

それでもFileMakerでは試行錯誤はやってもいいとは思います。しかしながら、試行錯誤をしっぱなしでは良くありません。試行錯誤した結果を設計という俯瞰した見方の情報として、ドキュメントなり、あるいはリレーションシップの構造やスクリプトへのコメントなどをで残すことによって、改善させる方法はいくらでもあります。そしてもちろん、要求を最初にきちんと揃えることの重要性は言うまでもなく、その点からもFileMakerで作る場合でもドメインモデルから一般的な設計を作って、そこからFileMakerの制約と向き合うという設計方法は意味があると考えます。

次回は、INTER-Mediatorを使った場合の設計を考えます。

[続開発プロセス#11] MVCパターンベースのWebアプリケーション

これまでの流れで、要求から設計を進めてきました。先にデータベースのスキーマを考える点では、SQLデータベースを使う的なことが早めに決まっているような流れになっていますが、GUIのモックアップから必要な機能を出して設計としてまとめるということをやってきました。ただし、原則としては開発環境の制約に関係なく、要求を元にした必要かつ実現可能性があるような汎用的な設計までができました。ここから、実際に実装が可能な設計を検討します。まず、このような例の場合一番わかりやすい、MVCあるいはMVC2パターンを検討します。

MVCパターンでの制約は、まずは文字通り、モデルとビューを分離して、コントローラーでそれらを統合するという構成にするということがあります。これは制約というより、一種の設計方針として認識されてており、制約という何かできなくなるような「縛り」というよりも、設計を進めやすくする「方針」のような捉え方をされている方も多いと思います。

そして、多くの場合は、HTMLのテンプレートをベースに、クライアントに送り出すHTMLコードを生成します。HTMLがビューであるという見方と、ビューによって生成されたものという見方があり、フレームワークによってその辺りの位置付けは微妙に違っているかと思います。また、テンプレート処理を行うのがサーバー側かクライアント側なのかという点も制約は異なると思われます。ここでは、サーバーサイドで完結するタイプのフレームワークを使っているとしましょう。

主要な制約としてはこのくらいではないかと考えられます。それ以外は、通常は開発言語で記述をするので、自由度は高くなります。巨大なクラスを作っても、小さなクラスに分けても、動くと言えば動きます。

まずは、モデルの抽出です。本質的には、MemoList、PickingUpのクラスで必要な機能が抽出されているので、それを受け付けるという処理を考慮し、テーブルとして用意するmemoとcategoryにそれぞれモデルを割り当てるのが素直な方法と考えられます。CategoryModelの最後の2つのメソッドは、ポップアップメニューの選択肢を構築するためのデータ取得のメソッドになります。

続いてコントローラーとして、MemoListController、CategoryListControllerを導入します。ここで、レイヤー構造となるような大きな区分をパッケージで分類しておきました。UI側の要求をコントローラーで捌き、データベースとのやりとりをモデルによって処理しやすい形式に整えるといった典型的な設計になります。ですが、これは、単にクライアントからの処理が関係しそうな箇所を繋いだだけであって、意図が正しいかを検討しなければなりません。

WebのMVCフレームワークの1つの大きな制約は、クライアントサイドではHTML/JSの世界であり、それがサーバー側でPHPなりJavaなり、異なる世界が展開されていることです。その橋渡しは、テンプレート処理ですが、テンプレート処理はページ構築時に全てを構築することにフォーカスしがちです。

しかしながら、ページを提示後の様々な処理にどう対応するかを、ページのテンプレートの段階で検討しておき、対処をしなければなりません。その後に、HTMLの部分的な要素をテンプレート処理して得ることもあるかもしれませんが、一方、単にJSONで送って返ってくるようなWeb API的な動作が欲しい場合もあるでしょう。

そこで、ここでまず、次の図のようにHTMLのテンプレートを設計します。ページ内の要素に適当に名前をつけてテンプレートそのものをモデルとして記述してみます。事実上、1つのクラスは1つのタグ要素に対応するものに近いレベルで記述しましたが、動作上必要な要素だけが抽出されています。なお、クラス名はタグ名でもいいような気もしますが、ここではそれらしい名前を記述しました。ルートはPageTemplateあり、これがbodyタグと考えても良いでしょう。モックアップの左側にあるカテゴリのボタンが並ぶ部分はCategoryBox、右側でメモの一覧が見えている領域はMemoListとしました。Memoの1行ごとにMemoLineがあり、メモの文字列はMemoTextです。このモデリングは、比較的細かく考えた方が良いでしょう。とは言え、これを書くのは結構面倒なのは確かです。できるエンジニアはこれを頭の中でさっさとやってしまうわけですが、今は開発プロセスの検討をしているので、あえて書いてみました。メモの文字列を表示するinputタグはMemoTextクラスに相当しますが、入力された文字列はvalue属性で得られます。これはブラウザ側の標準機能であるので、ここではprivateの記号で記述しましたが、スコープの意味ではなく、ここでは既定義されているという意味合いでマイナス記号を使いました。

テンプレートの要素をよく見て、その要素が何らかの応答をしなければならない場合には、HTMLelementステレオタイプのクラスはメソッドを記述することにしました。前の図のように、MemoListは書き換えればデータベースへ反映しなければなりません。そういうことで、MemoLineの子要素は全て、update()メソッドを持つ必要があります。update()メソッドのスペックとしては、書き直したら、どこかのタイミングでサーバーに修正データを送り、データベースを更新する必要があるということです。一方、CategoryBox以下のボタンでは、ボタンをクリックすることで、メモ一覧を新たにする必要があり、その意味で、updateMemoList()メソッドの定義があります。さらにこれだけで十分ということではなく、カテゴリが階層化されていて、大分類を変更したら小分類の選択肢が変化するという仕組みが必要になります。つまり、大分類のポップアップメニューを選択すると、小分類のリストをどこかから取ってきて、選択肢を入れ替える必要があります。実装方法はいろいろありますが、この小分類の更新を行うためにMemo_MajourCategoryクラスに、updateMinorCategory()メソッドを定義します。こうして、ページの初期状態をテンプレートとして記述するのはもちろんですが、それがページとして展開された結果を想定して、表示後に必要とする機能をモデルに組み込むことを行います。なお、UI Componentステレオタイプのものは、ここでは詳細設計対象外とします。

テンプレートから元のクラスを検討しましょう。次の図は、テンプレート処理した結果をクライアント側に展開しました。ここで、Webアプリケーションとしての制約があります。クライアントとサーバーの間はHTTP通信によってのみコミュニケーションを取ることができるということは実は大きな制約です。MVC系フレームワークを使う場合、もちろん通信はURLによって柔軟に作り分けることができるのですが、機能を組み込むときに最初に考えることは、それぞれの通信処理でテンプレートを使うかどうかです。ページ全体を生成するときにはテンプレートを利用するのは当然ですが、一方、修正したデータをデータベースに反映する作業はテンプレートを使わなくても良いでしょう。以前だと、サーバーの通信後に常にページ更新をしていたので、それは常にテンプレートを使っていたということになりますが、AJAXを出すまでもなく、現在の仕組みでは単にWeb API的な通信の実装はそれほど難しいことではありません。

ここで、まず、ページを表示するときには、テンプレートをベースに、初期的なメモの表示ルールを適用して、ページ全体を表示します。そのためのメソッドを、MemoListControllerにgetMemoListPage()メソッドとして定義するとします。テンプレートを利用したHTML生成を行って返すことを示すためにTemplatingというステレオタイプをつけておきました。そして、このメモリストでは左側の小分類を押して表示するボタンや、全部のメモを表示するボタンは、同じようにgetMemoListPage()メソッドを使ってページを書き直すのが効率的な設計ではないかと考えられます。この「ページを全部書き直す」作業は、基本的にはHTML上のリンクであり、そのリンクがサーバー側のメソッドを呼び出すので、コントローラーに直接つないでしまっています。ページネーションはコンポーネントとして利用すことにしていましたが、このページネーションも、getMemoListPage()メソッドを使ってページ全体を書き換えるのが1つの方法です(できればリスト部分だけを更新したいですが)。ここの実装はこの設計では細かくは追いませんが、方針としてはMemoListControllerにつながることで進めることにします。

一方、メモの文字列や日付時刻、そして分類の選択肢は、変更をすると、その結果をデータベースに書き直したいわけです。ただし、その作業は、自動的には行えず、要素上で発生したイベントに応じてクライアント側のプログラムを呼び出し、そしてサーバーに要求を伝える必要があります。そのために、クライアントサイドにEditingSupportというクラスを用意しました。もちろん、inputタグ要素の種類に応じて適切なイベントによってこのクラスのメソッドが呼び出されるように、実現可能性を加味した設計にしなければなりません。そして、EditingSupportでは、サーバー側のMemoListControllerにあるupdateMemo()メソッドを適切なパラメータで呼び出すように作ります。こうして、要素の変更からそれがデータベースまで更新される流れがクラス図上で明確に現れてきました。大分類のポップアップメニューを選択した場合、小分類の選択肢を更新するという作業には、ここではPopupUpdateSupportクラスを用意しました。

大分類を示すMemo_MajourCategoryからコールされるのですが、一方でMemo_MinorCategoryのポップアップメニューの選択肢をコントロールできないといけません。select側あるいはoption側のどちらでもいいので適切な参照が必要になり、あらかじめ参照を配列等に記録するか、呼び出し時に更新する要素を指定するなどの方策が必要になります。詳細はここでは省きますが、いずれにしても、JavaScriptで実装すべき内容が、独立したクラスで明確になりました。クラス図なのでクラスで書きましたが、実装じは単に関数でも大きな違いはないと思われます。

ここまで設計を進めれば、MVCフレームワークだと概ねどのフレームワークでも、似たような作業で実装を進めることができるでしょう。フレームワーク特有の事情がある場合は、もちろん、それも考慮して設計を進めます。通常はオブジェクト指向プログラミング環境ですので、クラス図で作った設計との親和性が高いのは当然のことと思われます。

現実の設計では、ここでのコントローラのように、テンプレート処理をしたりしなかったり、あるいは更新処理を受付たり、部分的なHTMLを生成したりと、メソッドによって様々な動作を設計することになります。ここではコントローラーにまとめて書きましたが、むしろメソッドごとに1つのエンティティとして記述した方がわかりやすいかもしれません。フレームワークを利用する場合には、テンプレート処理でスマートにページ生成ができることが強調されますが、ページ表示後の動作についてはそれ以上にたくさんの解決すべき問題を生み出します。フレームワークを使うことで簡素化される面はいろいろあるかとは思いますが、テンプレート処理後の動作を詳細に検討するという作業を、試行錯誤でやっている人は多いのではないかと思います。このように、モデルベースで考えてみれば、どんな仕組みをクライアントとサーバーに持たせるかは全体を見ながら検討できるわけで、より良い側面は多々あります。特に、うまく設計できないで悩んでいるなら、まずはモデルとして設計することは強くお勧めできます。

次回は、同じ設計を別の環境に適用することを考えてみます。

[IM] INTER-MediatorでFileMakerを使うときの注意

FileMakerは、テーブル、TO、レイアウトといったオブジェクトを利用する。Web共有からテーブルを直接指定はできない。また、TOも指定できない。指定できるのはレイアウトのみである。レイアウトが事実上のテーブルに相当するエンティティとなる。したがって、定義ファイルのnameキー、viewキー、tableキーには、レイアウト名を指定する。たまたま、同じ名前のテーブルやTOがあっても、FileMakerのWebアクセス時に実際に利用するのはその名前のレイアウトである。

フィールドのタイプで、日時の記録用に「日付」「時刻」「タイムスタンプ」の3つがある。Web経由では、「月/日/年 時:分:秒」のフォーマットにしなければならない。また、日付フィールドに時刻を含む文字列を入力したり、時刻フィールドに日付を含む文字列を入力すると、エラーになり処理は何もなされない。その「日付等の書式エラー」の番号が500なので、余計なことを考えてしまいそうだが、この500はFileMakerが規定したエラー番号である。

[続開発プロセス#10] UIの大枠から機能を検討する

少し間が空きましたが、開発プロセスの中で、まずはUIのモックアップを中心に要求を固めて、そこから設計に入る場合に、まず、データベースのスキーマから入る方法があるというのを説明しました。レイヤー構造で構築する場合、データは低い位置にあるので先に決めるという点に意義はないとは思いますが、一方で、あらゆる状況を加味したデータベース設計ができるのかという問題もあります。ですが、ここでは先に決めるという流れでプロセスを追っています。

データベース設計ができれば、そこに対してどんな仕組みをかぶせることで動くアプリケーションができるのかということを考えます。UIの設計から、まずは目に見える機能を取り出し、それをどのように構成するのが良いのかを考えます。ここで、「機能の一覧」を書き出すことが1つの方針としてはあり得るのですが、その機能をある程度分類しないと、構成が定まりません。そこで、メモのページ全体をMemoPoageとして、それらが、メモ一覧を示すMemoListと、左側のカテゴリ一覧を示すPickingUpの2つのクラスのコンポジションであるという見方をします。まず、目に見える範囲を狭めて、開発単位となるブロックを見つけるということです。ここで、一覧だと、1行のコンポーネントが複数あるようなことを示さないといけないのかと言えば、これは経験則的には不要だと考えます。ここで「リスト」と言ってしまうことで自明だからです。言い換えれば、1行の要素が繰り返されるということ自体は、そのクラスが持っているという見方をして、過剰に細かくは書かない方が設計は素直で読みやすくなるということです。データベースのテーブルを記述する場合に、クラス図では1つのボックスしか書かないのと同じ理由です。しかしながら、何らかの定義はどこかにあるわけですので、その点も書きたい場合はメモを追加するか、あるいはクラス名の意味をどこかに記載すればいいでしょう。なお、このようなクラス図からオブジェクトを図を想像できるかどうかということは、ダイアグラムの理解には重要なポイントであることは間違いないのですが、設計上どうすればいいかを詳細に決めると多分面倒なだけになりますし、大雑把にするとチーム内でコミュニケーションが中途半端になる可能性があります。機会があればこれは検討したいテーマです。こうして作成したのが以下のクラス図です。

緑色の3つのボックスがUIのブロックを1つのクラスとして記述したものです。中身は、ボタンや編集など、ほぼ、ページ上でのイベントに対応するもの、そしてそのリスト自体を生成するためのものが記載されているだけなので、細かくは説明しません。それぞれ、どのテーブルを利用するかで2つのテーブルに線を引きました。また、MemoListは、ページネーションと日付時刻のピッカーを、別に用意されているUIのコンポーネントを使うことにします。ここでは、MemoList上にある要素とは別のもの(ここでのページネーション)や、要素の動作を補助する必要があるものを(ここでの日付時刻ピッカー)抽出します。ただ、このUI Componentsは、実装先によって実態は異なると思われます。ここではまだ実装を加味しないということで進めてきましたが、実際にはこのように実装先の状況が徐々に現れることになります。プロセス的にはまず設計をして、実装に関わることを検討しようということを書いていますが、線引きは明確にはできないと思います。ここで重要なことは要素として抽出しながらも、一定の範囲で実装を意識したという点をメモにするか、きちんと意識しておくことです。

ここまでの設計の図を見る限りは、明白にレイヤー構造です。このような簡単なアプリケーションなら、もう実装は簡単にできると言っても良いでしょう。手慣れた方ならここまで図にしなくても、頭の中で設計ができてしまっており、自身の知識を持ってすればすぐに実装の作業ができるはずです。こういう状態を「スキルがある」ということになるかと思います。そのような状態の効率良さは当然ながらあるわけで、その結果「設計は不要」という考えにもつながるでしょう。設計不要論については今回は扱わないつもりですが、設計の行為を体系的に記述するため、ここで終わらずにさらに設計の検討を進めるのが一連の記事の目的です。よって、まだ続きます。次は、実装環境の制約を加味した設計を作っていきます。

SELinuxを避けずにINTER-Mediatorの稼働ができた

CentOSはSELinuxが既定値で機能しており、ちょっとしたことでも阻止されます。例えば、Apacheのドキュメントルートを/var/www/htmlとは異なる場所に設定したい場合、Apacheの設定以外にSELinuxでの許可を得る必要があります。セキュリティは重要とわかっていても、「ちょっと試す程度だしいいか」ということで、SELinuxの機能をオフにしてのその場しのぎはきっと世界的に普通に行われていると思います。SELinuxでググって見れば、オフにする方法のサイトばっかり出てきます。

実は最近、ある場所で、LinuxをセットアップしてWebアプリを納品することがあったのでした。INTER-Mediatorではなく、スクラッチから作っているWebアプリですが、コマンド呼び出しなどを多用するもので、SELinuxでの運用をまずは目指したものの、ちょっと行き詰まったのでした。そこでどうするか? 恐る恐る、SELinuxはなしでいいかを聞いたら、あっさりOKだったのです。セットアップ時に忘れないように切らないとと思っていたら、最初からオフだったし、Firewallは完全オフだったりと、清々しいVMだったりして、もしかして、世間的にはそんな感じなのか?とも思いつつ、ちょっと安心したりしました。

しかしながら、『INTER-MediatorはSELinuxでは動かない』となってしまうのも良くないので、サンプルの多くが稼働できる状態まで可能なポリシーファイルを作成して、レポジトリに掲載するところまでができました。その記録を書きますが、私自身はSELinuxの全貌を理解しているわけではなく、むしろほんの一部しか理解していません。実際のアプリケーションを動かすためのプラクティス的な情報しかありませんが、SELinuxで悩んでいる人や、将来のINTER-Mediatorのメンテナンスのためにブログを書いておきます。ちなみに、以下の作業は「 sudo yum install policycoreutils-python」でパッケージを1つ追加しないとコマンドが使えません。

SELinuxは、非常に大量の設定があるというのはまずは知るべき事実でしょう。最初に書いたように、ドキュメントルールが/var/www/htmlであるという定義があり、別の場所をルートにしようとしたら、この制約をオフにするか、別の場所に許可を与えるなどの処置が必要になります。OSやサーバー等の至る所に設定がなされており、全体像を把握するのはまず無理と思われます。設定ポイントというか、1つ1つの制約機能には、階層的に名前がつけられていて、場合によってはそれを読み解く必要もあるかもしれませんが、その方法は別のブログ等に委ねます。まずは、大量の設定があって多くは「禁止」状態になっており、必要なものを「許可」するというファイアウォール的な作業が必要であるということを理解しておく必要があります。

そこで、SELinuxの世界では、Boolean値と呼ばれれる定義定数があり、個別の設定よりも、もう少し粒度が荒いレベルでの許可を、論理値なので簡単に設定できるようにすることを意図した仕組みがあります。PHPのクラス等でメールを送りたいのなら、「sudo setsebool -P httpd_can_sendmail 1」コマンドを入れます。httpd_can_sendmailというのが既定値の論理値変数で、この値が1なら「許可」となります。通常はこの値は0になっていてメール送信は禁止されていますが、このコマンドを入れればメールを送信できるようになるという具合です。なるほど、それじゃあ、変数を探せば…と最初に考えるわけですが、変数が多すぎて訳がわかりません。変数名から「Webサーバーの」などと言った情報はあるので、頑張って読めばいいかと思いましたが、あまりに多過ぎて諦めました。ググっても「この変数です」というのが全ての場合では分かるまでには至りません。この方法は、簡易的に対応可能な場合だけに限るようです。

次に目をつけたのは、SELinuxでのログ(/var/log/audit/audit.log)を元に、禁止していることを許可するような設定を自動的に取り出してくれるという方法です。方法はいろいろ取れると思いますが、アプリケーションをインストールして、いろいろな作業を行い、そこで禁止されたことを拾い出すという仕組みが用意されているのです。「sudo audit2allow -a」のようにすれば、ログから得られた禁止情報を元に、許可すべき項目などを画面で見えるのですが、「sudo audit2allow -a -M ファイル」とすれば、カレントディレクトリに「ファイル.te」と「ファイル.pp」の2つのファイルを作ります。これらのファイルには、禁止された項目を許可するための設定記述が入っています。そして、「sudo semodule -i ファイル.pp」により、収集した許可設定をシステムに与えることができます。.teファイルが人間が読めるもので、それをコンパイルしたものが.ppファイルです。ちなみに、.teはテキストの略ではなく「Types Enforcement」の略だそうです。.ppは「Policy Package」です。つまり、audit2allowにより、禁止されたポリシーを許可する設定を作るということになります。自分で.teを編集して.ppを生成することもできるのですが、ほぼ、操作をした結果で必要な許可のための記述は得られるようで、操作、audit2allow、semoduleをひたすら繰り返し、エラーが出ないような状態になるまで地道に繰り返しました。また、アプリケーションをいくつか変えるなどして、なるべく多くの機能をカバーできるようにしました。本当はこれもきちんとプランをした方が良いのでしょうけど、まずは完全なものではなく実用になるものと思って手作業を進めました。なお、audit2allowでファイルは更新されますが、元のログファイルが同じであれば、以前に生成された定義は常に入ることになるので、同じファイルに気にしないで何度も定義の生成をしても構いません。

audit2allowでは、論理値変数についての情報も得られるのですが、生成された.ppファイルをsemoduleコマンドで取り込むことで、論理値変数の設定は特にはしなくても良いようです。いくつかのサイトを見ながら作業を進めたのですが、「setenforce 0」でSELinuxをPermissiveモードにして、監査とログ投入は行うのだけども、エラーとしないで実施可能な状態にして、ログに溜め込み一気に.te/.ppファイルを作るという方法が紹介されていました。なるほど。ただ、今回は、どんな風に「動かない状態」が発生するかを確認することもあって、1つ1つやっていきました。

ただ、SELinuxの設定の中にはログを残さないというタイプのものもあるようで、実際、いろいろやっているとそういうのに引っかかったようです。そこで「sudo semodule -DB」と入力することで、全ての監査結果がログに残るようになります。このコマンドを入れないと、収集漏れが発生していたということです。INTER-Mediatorのレポジトリで、dist-docs/selinuxディレクトリに生成したファイルを入れてあります。

もちろん、単に許可するとだけだと、セキュリティ的に甘くなるのではないかということも十分に考えられます。ポートを開くと言っても、決められたポートだけにしたいというセキュリティ上の要求はあるでしょう。それをSELinuxで賄うことで、INTER-Mediatorで作るWebアプリ以外での制限はしやすくなるものの、自動生成したポリシー設定からさらに難易度は上がりそうです。SELinuxを無効化するよりも遥かにましだということで納得しようと思います。

参考にしたサイトは以下の通りです。ありがとうございます。こちらの方が、本稿よりも詳細な情報が得られます。

[続開発プロセス#9] データベースの設計から入る

前回までに、サンプルとして作るメモアプリケーションと、その要求項目をまとめました。本来は、そこから「要件定義」をするところかもしれませんが、要求+UIデザインがあれば、基本的には要件と呼ばれる項目もピックアップされていると考えて、今回はシンプルなアプリケーションでもあるので、割愛します。ここから分担して作業するような場合には確かに要件定義も必要かもしれませんが、ここで紹介しているアプリケーションだと表現の違い程度のものしかありませんので、文書化する手間を省きます。

実際に作成するものが決まって来れば、全体の機能からだんだんと詳細化する様な方法がまず1つあります。これに対して、まずはデータベースのスキーマを確定させ、ドメインをデータベースの中に実現することを優先する方法もあります。複雑なアプリケーションの場合は前者のように、機能を分析して、どういう構成をするのかということを先に考え、並行してデータベースの設計をするのが良いと思われます。一方、シンプルなものは、データベースをまずは設計して、UIとDBの間をどのように埋めるのかということを考える方が効率が良いと思われます。ということで、今回は、まずはER図レベルのものを作ることにします。要求項目等から、以下のようなER図あるいはクラス図を作成しました。

前者はER図、後者クラス図です。ここでは、どんなテーブルがあって、どんなフィールドがあって、関連付けをどう行うかということが記載されていればいいので、どちらの図でも大きな違いはないと思います。見ての通りAstah*を使っているので、どちらかと言えば、クラス図の方が自由度が高いこともあって、私は通常はクラス図で記述します。クラス図では付加的な情報はステレオタイプを活用することで、ちょっと見慣れるのに時間がかかるかもしれませんが、記述は可能なので、そうした仕組みを用意しています。

要するに、メモとカテゴリの2つのテーブルがあるということです。メモに日付時刻のdtフィールド、メモのmemoフィールドがあるのはいいとして、2レベルのカテゴリ設定のために、2つの外部キーフィールドtop_category_id、sub_category_idを用意して、カテゴリつまりcategoryテーブルのcategory_idフィールドに関連づけています。カテゴリは、目に見えるデータはcategory_nameだけですが、階層化の管理をここではテーブル内に持たせることにします。本来は、カテゴリとカテゴリ間の関連を別々のテーブルで表現するのが役割を分離した設計になるかもしれません。ですが、2レベルということで、主キーとなるcategory_idと、自分の上位のカテゴリを示すparent_category_idのフィールドを用いるだけで概ね問題ないと判断して、このようにしました。parent_category_idがNULLであれば大分類、category_idに存在する値があれば小分類とすることができ簡便です。もちろん、1つの小分類項目は、1つの大分類項目にだけ所属できるので、完全にツリー状の構造になるでしょう。前回のUIサンプルのカテゴリ一覧(ページ左側)にあるような大分類、小分類の構成にするには、例えばテーブルのデータは次のようになります。

category_idparent_category_idcategory_name
1仕事
2ホーム
3趣味
41毎日
51レポート
61進行中
71気付き
82レジャー
92学校行事

本来、こうした状態をキープするためには、カテゴリの修正ページはそこそこ複雑になります。もちろん、名前を書き直すのは容易ではありますが、大分類を消した時、小分類も一緒に消すのかどうか、あるいは残すのか。また、それぞれの分類を消した場合、その分離を設定しているメモの方はどのような扱いにするかなど、検討事項はいろいろありますが、今回の設計例ではこれも省略します。ちょっと省略しすぎかもしれませんが、お許しください。

メモを一覧表示する部分はあまり難しさはないと考えますが、結果的に残るのは、分類のリストからの検索と、新規入力時に大分類と小分類のポップアップメニューがあることです。後者は、大分類と選択すると、それに所属する小分類のポップアップメニューに更新しなければなりません。しかし、この段階では、まず、そうした具体的に実装されるべき機能を把握することに進みます。

Chart.jsのチャートのサイズ増加が止まらん!

Chart.js v2.5使ってます。久しぶりに開いたページの中にChart.jsによるレーダーチャートがあるのですが、描画後、チャートがビヨーン、ビヨーンとサイズがデカくなるのでした。以前はそんなことはなかったはず、なんでやねん! 変なプログラムを書いてしまったか?と思ったけど、そうではない。デバッガを見ていると、canvasのwidthとheight属性が、すごいスピードで+1ずつインクリメントしているのです。durationの値は0にしている。そこに100とか数字を入れても同じだったので、これの問題ではないということですね。かなりあれこれ探したら、なんと、公式ドキュメントに書いてあるじゃないですか。「canvasのコンテナはposition: relativeにしなさい」ということで、なんとか、ビヨーンはなくなりました。しかし、もう、画面に何も表示されないくらい、チャートがデカくなっていくのは悲しい画面でしかないですね。ということで、Chart.jsのcanvasは、divなどで囲ってください。

[続開発プロセス#8] 設計作業の実例(ドメイン分析とUI設計)

ここからは開発プロセスに従って、実際にどのように設計を進められるかを検証しながら、問題点やポイントを把握して行くことにします。サンプル開発のテーマですが、あまり難しすぎると把握が大変で、プロセスそのものとは関係ない情報を大量に捌くことになりかねないので、なるべくシンプルなものにしました。テーマは「1行メモ」です。もちろん、ものすごく簡単なので、すぐに作れるという方は多いとは思います。とはいえ、小さな開発でも突き詰めればきりがありません。要求をしっかり掘り下げるといくらでも細かな点は抽出できます。過剰に複雑にならないように、とは言え、ステークホルダーの要求に答えるというプロセスを考えるためには、一見するとすごく簡単なテーマがいいのではないかと思います。

以下、まずはINTER-Mediatorで構築することを中心に考えます。モックアップも、INTER-Mediatorを使って作りますが、データベース定義などしないで、モックは単に見栄えのチェックのためのものだけを作ります。

まずは、最初の要求が発生するとして、次のように要求が出てきたとします。

  • [R1] 1行メモを複数記録でき、一覧で見ることができる
  • [R2] メモ作成あるいは変更時の日時を記録することができる
  • [R3] 1つ1つのメモは、大分類と小分類を設定することができる
  • [R4] それぞれの分類は、予め定義されたものから選択するようにできる
  • [R5] 小分類の項目は1つの大分類項目に所属する形式にする(大分類と小分類は階層的な関係になっている)
  • [RX-1] 利用者、1人1人の1行メモを管理でき、他人のメモを見たり修正できたりはしない
  • [RX-2] 大分類、小分類の項目を編集する機能が必要

項目にコードを割り振っています。RはRequirementのつもりですが、RXの番号があるものは、要求としてはあるけども、設計作業を単純にするために、以後は無視するものとします。思いつく全ての要求を入れてしまうと、プロセスよりも状況の把握の方が膨大な情報量になるので、その点はご容赦ください。もちろん、無視した要求が重要なこともあるかと思いますが、ここでは、プロセスを通すことをまずは説明したいと思います。

上記のような要求がある場合、ざっくりとモックアップを作ってみるとこんな感じでしょうか? INTER-Mediatorを読み込んでいるので、テーマが適用されていますが、あとは単にinputやselectタグを適当に並べているだけで、特にデータベース処理などはしていません。要するに、tableタグにtrタグが3つ含まれていて、「複数のメモがある」感じを出しているだけです。

こういうのを作ると、もう、ここでいろんな要求が増えてきます。ちなみに、「メモ」あるいは「リマインダー」って、多くの人は「いいアプリがない」とお悩みなようで、どうやら、一人一人が要求するスペックが多岐に渡っていて、落とし所が見つかりにくいテーマなようです。ということで、以下、要求が出てくるとは言っても私の好みという色彩は強いので、メモアプリケーションとして評価はしないでください。あくまで、プロセスとしてこういう流れがありうるのかどうかということです。以下のような要求が追加されました。

  • [R6] 一覧に表示するメモは、分類に応じて絞り込みができるようにする。
  • [R7] 分類に関係なく全部のメモを表示したい。
  • [R8] メモがたくさん出てくる可能性がある。だとしたら、10ずつ表示するなど、ページネーションが必要になる。
  • [R9] 日付の入力はキータイプだけではなく、JavaScriptのコンポーネントを利用したい。

これらを含めたモックアップとして、次のように発展させたとします。絞り込みのため、左側に分類名のボタンを用意します。ページネーションは「とりあえず配置する」ということを示す赤い文字列を示すに留めています。一定以上の労力がかかる作り込みは、モックアップでは避けた方が良いでしょう。その代わり、例えば、このように赤字でコメントを書くということを行うことで、コミュニケーションを図ります。

この辺りまで進んでくると、おそらくディテールについて、いろいろな意見が飛び交うでしょう。まず、前の図だと、分類やメモそのものはテキストフィールドにありますが、最初に書き込んだ文言から変更しないことが多いのだから、テキストフィールドである必要はないと言えます。また、文字数が長くなると、欠ける可能性もあるので、メモの一覧には編集機能を付けないのがいいのではないかということになります。

であれば、入力や編集はどうするか? 1つの方法としては、以下の図にあるように、メモの一覧の下などに、メモの入力用エリアを設けて、ここで最初の入力ができるようにすることになるでしょう。他にも方法がありそうですが、ここでは別領域を設けることで合意したとします。

  • [R10] メモの一覧は、表示のみにする
  • [R11] 新しいメモを入力するためのUIを、一覧とは別に用意するタイプにする

左側の絞り込みを見てみると、小分類での絞り込みはできそうですが、指定した大分類だけの絞り込みもしたいのではないかということになりました。要求についてはR6が詳細化されたとみることができるでしょう。

  • [R6-m1] 一覧に表示するメモは、分類に応じて絞り込みができるようにする。この時、小分類だけでなく、大分類を指定しての絞り込みができるようにする

入力と表示を分けましたが、このままでは、メモの編集ができません。書き損じた場合はどうするのということもありますし、分類を変えたい場合も手段がありません。要するに、CRUDの考慮が必要になるということになりました。項目の削除は、一覧に「削除」ボタンを作る。そして、項目の内容を変更することは、どちらかと言えば主要な操作ではないので、リストをクリックするか、あるいはボタンを押すなどの方法で、別のページに遷移して修正ができるようにしようということになりました。ただし、この別ページ部分は今回の流れでは追わないことにしましょう。

  • [R12] 一覧にあるメモを「削除」ボタンで削除できるようにする
  • [RX-3] 既存のメモの内容を変更するための編集ページを用意し、遷移して修正できるようにする

他にはどうでしょうか? リストや分類項目の並べ方が確定していません。これについてもやはりいろいろな意見があるでしょうが、次のようにしましょう。

  • [R-13] メモの一覧では、作成修正日時の逆順で表示する。
  • [RX-4] 絞り込みの部分での項目の順序は、分類項目の編集時に指定できるようにする。

ちょっとしたアプリケーションのつもりでも、結構、項目は出てくるものです。ここまでのところでは、要求に関して、UI設計を進めることで、要求に不足する情報を補うことを意図した作業を進めてみました。メモについてはドメインと呼べるほどの知識はないのかもしれませんが、アプリケーションによってはそのドメイン部分の理解を何かの方法で進めながら、要求とUIの部分を進めました。次回は続いて、要件定義からモデル化を進める部分に進みましょう。

設計作業のサンプルで用いたファイルは、こちらで共有します。