[続開発プロセス#14] エンクロージャー/リピーターの制約

前回の記事で、HTMLのテンプレートの設計、つまりページファイルの設計を出すときに、非常に根深いところにある制約の話が必要であることに気付きました。ですが、前の記事が長いだけに、別途記述するということで、連投となりました。

話を少しすっ飛ばしたのは、ページのモックアップにある左側の、カテゴリ一覧の設計です。まず、オブジェクト指向的に正しくモデリングをするということを進めます。以下の図は、ともかく見えるオブジェクトとをクラスとして記述して、全体をまとめるであろうCategoryBox配下に並べたところです。オレンジ色のメモに記述したように、ここには矛盾があります。ここでは、「小分類は必ず何かの大分類に所属している」前提があるとしているので、このように、大分類に所属しない小分類が存在しえるモデルは、モデル自体が間違っているので、正しい設計とは言えません。

ここで、小分類は大分類に所属することをモデルとして表現してみます。小分類の名前とボタンがあり、それぞれ、MinorCategoryLabel、MinorCategoryButtonクラスですが、既に存在する大分類の名前であるMajorCategoryLabelに関連づけてみました。これだと、小分類の存在は大分類の存在を前提になっている状況は示しています。しかしながら、オレンジ色のメモに記述したように、小分類の中でラベルとボタンがバラバラであり、ラベルが4つでボタン3つでも成り立ちそうなモデルです。ここで、1つの小分類は、何らかの1つの対象が保持する形にであるべきではないかと考えます。

そこで、次のように、小分類の項目をまとめるMinorCaterogyBoxクラスを間に入れてみました。この辺りで分類そのものをデータベースのテーブルに入れていることを考えれば、MinorCategoryLabelなどフィールドに相当するものが、レコードに相当するMinorCategoryBoxにまとめられて、それが1つの大分類に所属するという状況をだいぶんと正確に示してきていることが伺えます。ここで、改めて大分類に目を向けます。ここでの大分類の名前を示すMajorCategoryLabelは大分類の1項目の中の1つのフィールドであり、属性の1つにすぎません。これ自体が大分類項目1つを代表するのはちょっと無理があるのではと考えられますし、小分類の類するから、「大分類の1つの項目をまとめるものがあれば良い」ことが考えられます。

そして、次の図のように、大分類の1レコードに対応するMajorCategoryBoxが存在し、大分類の1つのフィールドは、このMajorCategoryBoxから1対1で表現しています。また、1つの大分類から複数の小分類が関連づけられることも、MajorCategoryBoxとMinorCategoryBoxの1対多の関係として表現できています。ここで、エンクロージャー/リピーターの関係を1対多の関係に持ち込み、データベースのデータをバインドしたいと考えます。これはINTER-Mediatorの肝になる機能です。

ここで改めて、エンクロージャー/リピーターをモデルとして示します。HTMLのテンプレートは何がクラスで何がオブジェクトかというのは視点あるいは場面によって変わると思われますが、HTMLの記述可能な範囲において、エンクロージャーがリピーターを含み、リピーターにはターゲットノードが含まれる階層構造になっています。オブジェクト図で記述すると明白なように、これらはクエリー結果のリレーション/レコード/フィールドと言った階層に一致しています。

INTER-MediatorはDOMの領域でテンプレート処理を行っています。テキストのレベルで行うより高い粒度になりますが、一方でそのための制約が発生しています。このリレーションとテンプレートを合成するアルゴリズムでは、テンプレートにあるリピーターを一度複製を取って削除します。複製は所属するノードも含めるので、複製はテンプレートそのままにターゲットノードを含めて保存されています。そして、リレーションの中にレコードがあればその保存したリピーターを複製して、エンクロージャーの子要素とします。この作業を繰り返します。この流れによってリピーターがレコードの数だけ繰り返されて、一覧表示が完成します。

このとき、必須ではないのですが、エンクロージャーの子要素は全てリピーターである方が最終結果は予測付きやすいです。仮にエンクロージャーにリピーターでない子要素がある場合、現状は結果的にリピーターが繰り返す前に集まってしまいます。それでいいのかもしれませんが、そうなら、エンクロージャーの前に記述すればいいことなので、リピーター以外はエンクロージャーの子要素にはしないというルールは問題ありません。ところが、前要素→リピーター→後要素のように並んでいた場合、気持ちは前要素→展開したリピーター→後要素のようになって欲しいかもしれませんが、そうなりません。そこで、header、footerとなるリピーターやseparatorとなるリピーターも定義しています。とにかく、テンプレートで並べたリピーター以外の要素が展開後どうなるかを意識しなくてもいいように、リピーター以外は子要素にないのが良いと考えます。これは、INTER-Mediatorの大きな制約なります。

ここで、エンクロージャーとリピーターの展開は、1対多の箇所に対応づけられるのがわかったので、以下の図のように、ステレオタイプで、enclosure/repeater/tareget nodeを割り当てました。しかしながら、ここで、MinorCategoryBoxがリピーターなのですから、その上位であるMajorCategoryBoxがエンクロージャーになる必要があります。もちろん、そうすると、MajorCategoryBoxはエンクロージャー件、さらに上位のコンテキストに対するリピーターでもあります。この状態でINTER-Mediatorは稼働するのではありますが、こうなると、MajorCategoryBoxの子要素に、MinorCategoryBoxとMajorCategoryLabelの2つの要素が割り当てられます。前者はリピーターですが、後者はリピーターではなく、INTER-Mediatorの制約に反します。そこでどうするかを考えないといけません。

ここで、新たに、MinorCategoryConteinerクラスを導入します。つまり、レコードの数に応じて複数存在するMinorCategoryBoxクラスのオブジェクトをまとめる存在のものを用意します。HTMLではこの記述は、汎用タグのdivやspanで簡単にできます。すると、MajorCategoryBoxはMajorCategoryLabelとMinorCategoryContainerを含み、リピーターを含まないことになります。(なお、AllSelectButtonは本来はエンクロージャーであるCategoryBoxの外に出すべきです。この要素は、リピーターではないからです。)

このような、エンクロージャーとリピーターの間の制約があり、そのために、ここでは4段階のdivタグで囲まれるようなHTMLコード例を示したわけです。

この制約は一般には考えにくい事実かもしれません。HTMLは独特の柔軟性を持っていることを利用しているということと、やはりデータベースの検索結果であるリレーションという表形式のデータを自動的に展開するという仕組みが微妙にコンフリクトする場面でもあります。リレーションの結果を調整するのはもちろんですが、一方で、HTMLのテンプレートも、リレーションをマッピングできるように調整しないといけないということになります。

元々、INTER-Mediatorは、trタグの要素がtbodyタグ要素の子要素として繰り返すことで、レコードの展開を「繰り返し」の拡張命令的な処理をしなくても実現できることを発見したのが始まりです。tbodyタグの子要素にはtrタグ要素しか登録できません。それ以外のものはテーブルの外にはみ出ますし、定義上、trしか存在できないはずです。また、同時にselectとoptionについても同様な関係があるとして、これらの2組は自動的にエンクロージャー/リピータとして認識するようにしました。前述のカテゴリ一覧も、tableの中にtableを作る方法でできると説明してしまえが一言で終わってしまい、あとは試行錯誤してコンテキスト定義を行えば動くでしょう。しかしながら、data-im-control=”enclosure” / data-im-control=”repeater”により、どんなタグでもエンクロージャーやリピーターになりうるようにしたのですが、その結果、明示的にエンクロージャーとリピーターのタグを記述しないといけなくなってしまっています。デザイン的には不要でも、INTER-Mediatorのアルゴリズムに合わせるために、ここでのMinorCategoryContainerのような要素が必要になります。

[続開発プロセス#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つのエンティティとして記述した方がわかりやすいかもしれません。フレームワークを利用する場合には、テンプレート処理でスマートにページ生成ができることが強調されますが、ページ表示後の動作についてはそれ以上にたくさんの解決すべき問題を生み出します。フレームワークを使うことで簡素化される面はいろいろあるかとは思いますが、テンプレート処理後の動作を詳細に検討するという作業を、試行錯誤でやっている人は多いのではないかと思います。このように、モデルベースで考えてみれば、どんな仕組みをクライアントとサーバーに持たせるかは全体を見ながら検討できるわけで、より良い側面は多々あります。特に、うまく設計できないで悩んでいるなら、まずはモデルとして設計することは強くお勧めできます。

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

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

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

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

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

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

[続開発プロセス#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学校行事

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

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

[続開発プロセス#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の部分を進めました。次回は続いて、要件定義からモデル化を進める部分に進みましょう。

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

[続開発プロセス#7] ここまでのビッグピクチャー

ここまでに説明したことを図解してみます。以下の通りです。要するに「通常の開発」とさほどの違いはありません。UI設計を独立させていることと、実装設計モデルという考え方が入っているだけです。

ちなみに、「こんなにたくさんのドキュメントを作れというのか?」という気になるかもしれませんが、メリハリは付けるべきであるし、開発のイテレーションでは更新しないというドキュメントも出てくるかもしれません。むしろ、どの成果物に力を入れるべきかを考えながら、さらにこれから深掘りしようと思います。

そういうわけで、皆さん、良いお年を!

[続開発プロセス#6] 実装部分の設計の大まかな方針

開発においては要求が最初に何らかの方法で記述される前提で話を進めます。前回のポストでは、要求から具体的な機能や必要なデータベース設計を検討するために、UIの設計を進めることを説明しました。制約された状況(フレームワークや開発ツールを適用した開発)では、UI開発がいろいろな意味で容易にできる状況が用意されているので、UIの設計はプロトタイプを開発したり、あるいはモックアップを作ることで、エンドユーザーによる確認ができることを示しました。そのUI設計や元になっている要求定義をもとにして、どのような仕組みが必要かをさらに検討できます。もちろん、機能や非機能という言い方もできるでしょうし、要件定義という言い方もできますが、UI設計によるビジュアルな側面の具体化とは別方向に、実装すべき機能を抽出するための設計のプロセスが必要です。もちろん、ここでも制約された状況において適用可能な方法を考えて行きます。なお、具体的なモデリング例などは追々紹介するので、まずは全体像を示します。

まず、一般には、要求定義などの初期段階で得られる情報を元にして、ビジネスモデルやあるいはドメインモデルと呼ばれるモデルが作られます。最初に作成できるモデルは、システム境界は曖昧か決まっていない状態のもので、システムを取り巻く業務全体がどのような要素とそれらの関連性を持つのかということを記述するものです。一般にはクラス図で書かれるものが多いと思われます。この段階では、対象とするビジネスドメインを整理し、理解するためのモデルとなります。また、その業務領域における概念を整理し、特定し、名前を確認する(あるいは名前を付ける)ということを行います。なお、用語としてのビジネスモデルは、別の意味にも捉えられるているので、この最初のモデルを「ドメインモデル」と呼ぶことにします。

ドメインモデルにおいては、「どうやって実装するか」ということをなるべく排除する方が良いでしょう。一番の目的は、業務全体をモデルとしてどのように今現在捉えられているかということを理解するためのモデルであるからです。開発者がどうしたいのかということはなく、あくまで、現状のスケッチであり、顧客が考えるシステムを取り巻くビジネス環境を表現したものです。用語については、極力、顧客が使っている用語を使うべきで、ここで用語の整理はまずしない方が良いでしょう。顧客が使っている帳票などから読み取る作業を通じて、1つのまとまった対象が何か、それらがどのような順序で登場し消えていくのか、そして、何を手がかりにその対象が結びついているのかなどをモデル化します。ここで、若干の矛盾点があってもモデルとし、その疑問点はメモ等で残し、どこかで解決を図ることにします。矛盾点は整理せず、現状やプランに矛盾と思われる箇所がある点をモデルとして表現することが肝心です。

この作業は、要求を抽出した段階でもできますが、UI設計の結果を反映させることまでは考えなくても良いでしょう。要求やそのための調査の結果を整理するという目的のためであって、このドメインモデルは最終的な着地点ではありません。UI設計に基づく結果は、実装のための設計にステップは進んでいるので、初期段階での理解をするためのドメインモデルは、要求を元にして作成することにします。

ドメインモデルやUI設計、そして要求を元にした要件定義、あるいは機能や非機能の整理をした結果が得られます。これを元に、具体的な設計を進めます。UI設計を行った結果、まず、システム境界はかなりはっきりするはずです。要求としていろいろ出ているとしても、対象とするシステム外部のことは、設計には含める必要はありません。ただ、別のシステムとして将来実現するというようなことがあれば、要求の記述を改めてそれを設計に含めるということになります。

具体的な設計とは、例えば、データベースのスキーマ(ER図)や、実装につなげるためのクラス図、シーケンス図、コミュニケーション図などで作成することになります。この段階のモデルは一般には「設計モデル」と呼ばれます。ここで、設計モデルは、開発環境の制約を考慮しないものをまずは作成します。最初から制約を考慮するのは手間もかかるし、また、様々な考慮すべきことや解決のための発想を阻害する可能性があります。つまり、まずは一般的な設計を目指すということです。データベース主体の設計では、ER図に相当する設計だけで済ませるかもしれませんが、プログラムを記述するような場合はコントローラに相当する部分の機能の抽出とモデリングもきちんと行うべきです。この段階では、デザインパターンやアーキテクチャパターンなどの様々なテクニックを駆使することも必要になります。

こうして設計モデルが確定が近くなると、実際に開発で利用する状況に合わせた設計モデルへと変化させます。このモデルを「実装設計モデル」と呼ぶことにします。設計モデルに対して、制約を適用したモデルへと発展させて作ります。例えば、モデルやコントロールのクラスを継承するような場合、詳細な設計が必要なのはコントロールそのものではなく、継承して追加する部分だけになります。設計モデルでは「必要な機能を持ったコントロール」が、実装設計モデルではフレームワークが提供する部分はむしろ割愛し「実装作業が必要な部分の記述に絞ったコントロール」として定義し、既存のクラスから単に継承等で記載するだけにします。もちろん、そうすることで、実装の作業をスムーズにすることを意図しています。FileMakerの場合は、ER図を記載した上で、リレーションシップに記述するオカレンスの図に発展させるのが、設計モデルと実装設計モデルの違いになります。いずれの場合でも、開発環境に合わせたパターンなどの設計ノウハウの適用が必要になります。この領域でのノウハウは、ともすると長年のノウハウといった俗人的な対応がよく見られますが、汎用的な開発と同様、パターン集の充実など、環境に合わせた開発ノウハウの標準化をしておくことで、スムーズに実装設計モデルを構築できるでしょう。

このように、設計においては、ドメインモデル→設計モデル→実装設計モデルといった段階を踏むことで、設計を進め、制約された状況でのモデルを構築することを提唱します。特に、設計段階で、どこで制約を適用するかということについては、設計の後の段階で行うがポイントになります。言い換えれば、ツールや環境を考えずに、まずは汎用的な設計をした上で、その設計を実際の環境に適合させることにより、ツールや環境ありきの限定された実装を避けることを意図しています。どの段階でも、モデルに落とし込むテクニックとしてはパターンがまずは挙げられますが、実装設計モデルへのパターンについてはなかなか用意されていないのが実情であり、そのパターン化を意識しながら作業することにもなるかもしれません。そして、ここで本当に欲しいのは最後の実装設計モデルであるとも言えるので、そうなれば設計モデル自体は時間をかけて仕上げるのではなく、不足がないようにすることを心がけてあまり詳細化しすぎない程度で終わらせておき、実装設計モデルに必要な部分だけの詳細化を進めると言った効率的な作業の段取りも考えられます。

[続開発プロセス#5] UIの設計はプロトタイプあるいはモックで

UIの設計はどうするか? 設計の成果物は何にするのか? これは難しい問題であり、そう簡単には結論できません。特定の形式に決めてしまうと、それだけで「自分はできない」「自分は関係ない」という気持ちに強くさせると思います。ですが、設計と実装はどこかで線引きは必要です。その線引きも、会社、チーム、案件、顧客、まずはステークホルダーの集団ごとに違ってくると思われます。そして、設計中も、実装中も、要求との適合を判断するための基準は、UIから得られることは多いでしょう。UIだけということではないものの、システム全体像を形作るものがUIでもあることから、UI設計は独立して進めることになります。

ここで、UIの設計を、制約された状況(フレームワークやツールを使う前提での開発)において適用する場合、まずは、プロトタイプの構築を目指すべきと考えます。なぜなら、それらのツールは一般にUI構築が容易であり、変更も容易であることを謳っているからです。その仕組みを設計においても使わない手はありません。また、Webフレームワークであるなら、Webページのモックアップを作ることを行います。どの手法で、どこまで作り込むのかということは、ツールによっても違ってくるとは思います。ですが、ここでは「実環境である程度作る」ということが重要と考えます。

よく、ホワイトボードや紙を使って設計することがあります。ホワイトボードや紙を使うのは悪いことではありませんが、それらを設計の成果物とすることには、以下のような問題があると考えます。まず、ペンで記述するという自由さから、実装不可能なデザインをやってしまう可能性があります。プロトタイプやモックアップを作るのなら、少なくとも見かけの上では、実現不可能なことは実現できない時点で判別します。そして、やはり手書きの問題点は、書き漏れが多いということです。ホワイトボードや紙を使って、特にグループワークをしていると、話しながら書くので、話を聞いていれば意味がわかる線の塊も、単にその「線の塊」を見ただけではなんのことかわかりません。また、場所が足りないので、「…」となっていたり、書き損じがあるなど、最終的な成果物としては不完全です。つまり、ディスカッション等に費やした時間が後々に無駄になる可能性が高いのです。

では、Excelで清書しようということになるのですが、ずばり言ってそれは無駄です。Excelでグラフィクスを駆使する時間があるのなら、プロトタイプを作ってしまいましょう。また、HTMLでモックを組めば良いことです。わざわざExcelで作ることで、時間をかけるのは無駄です。証拠を残すだけのExcelワークほど無駄なものはありません。ホワイトボードや紙でのディスカッション結果や検討結果は、記憶が新しいうちに、プロトタイプやモックアップに展開し、そこで表現しきれないことは、要求として記述を残しておくのが良いでしょう。

プロトタイプやモックアップをUI設計の成果とする場合、大きなポイントは、それによって何を明確にするのかを、予め目星をつけるあるいはチームとして合意をしておくということがポイントになります。プロトタイプはうまく作りすぎると、クライアントは「もう出来上がったのですか!」と期待に胸を膨らませることになり、ボタンを押しても何も起きないことにかえって幻滅します。おそらく、ここで機能の実装をガッツリやることは、避けたいと思うところです。なぜなら、UI設計をしている段階ではまだまだ多くのことが決まっていないので、実装結果が無駄になる可能性があるからです。よって、プロトタイプもモックアップもともかく、労力がかからない範囲にしないと、かえって無駄な作業をしたことになります。

プロトタイプやモックアップにって明確にすべきことのまず一番重要なことは、「項目」となるべき要素の抽出です。UI画面に入力枠があって、そこに入力するということは、「項目」の1つです。また、一覧表を考えたとき、その列に表示すべき情報も「項目」です。これらは、明らかにデータベースのスキーマや、あるいは内部動作でのプロパティに直結するものであり、UIの上で必要なものを可能な限り余すところなく、UI設計上に記述できていれば、設計のレベルとしては成功だとみなせるでしょう。また、可能であれば、クライアントに協力してもらって、実際に入りそうなデータを入れてみます。もちろん、プロトタイプでは制約も多いですし、HTMLのモックだと自由度は少ないでしょう。それでも、例えば、それらをホワイトボードに写して、そこに手書きでもいいかと思います。具体的にデータをいれる場面にならないと実業務との関連性を判断できない人は多い、というか、ほとんどの人はそうです。抽象的なデザインのモックを見て判断できる人は、一般ユーザーではなくその道の人です。プロトタイプやモックアップでは、項目の抽出が目的であることを常に示しながら、クライアントに的確な判断ができる素地を整えるのが、UI設計のセッションには必要ということになります。

次に明確にすべきこと、あるいはできることとしては、その場面で必要な作業です。狭い意味では、どんなボタンをつけておくと良いのかということになりますが、言い換えればページやパネルなどに実装すべき機能を特定するということです。ここでは、いろいろなワークフローを想定してもらい、そのワークフローがスムーズに進むように、仕組みを追加すると言えばいいでしょうか? そうなると、開発側がプロトタイプやモックアップを紙芝居的に見せながら、不足する機能をピックアップするというセッションが可能になります。ただ、現実位はワークフローの抽出はなかなか大変です。当然、ドキュメントになっていて手順になっていれば、作業はスムーズですが、多くのクライアントは「時間がない」とか、「自分は全部知らない」などの言い訳をして、そうしたドキュメントは作ってくれません。開発側で要求抽出の結果から、不完全なワークフローの記述を作るなどの方策も考えられます。結果的にその場に来てもらって確認ということになることが多いとは思います。このセッションは恐らく達成率はあまり高くならないでしょうけど、要求を実現するための仕組みをUI設計に落とし込むためには、どうしてもワークフローとの関係を早い段階から明白にする必要があります。プロトタイプやモックアップを使えば、一定の範囲内では可能と考えられます。

プロトタイプやモックアップを作った後は、潔く捨てるのか、それとも、それを元に開発を進めるのか? これは非常に悩ましいところです。理想的には1からやり直すべきでしょうけど、結果的にはプロトタイプやモックアップをベースに開発は進むことになります。ここで注意したいのは、プロトタイプやモックアップに入れたけど、結果的に不要だった要素を確実に排除しておくことです。また、データベースを含むプロトタイプもFileMakerを使うような場合はあるかと思いますが、操作が簡単なら、データベース設計はやり直すのが良いと思われます。数回後に説明しますが、別途データベーススキーマの設計はすることになるので、結果的に1から作り直すことになるでしょう。ただ、プロトタイプやモックアップの完成後の扱いは、慎重に検討しましょう。また、作りかけの素材を元にする場合も、プロトタイプベースの開発と同様に、既存の開発物をどのように使うのかという問題は付き纏います。