アーキテクチャ概観
概要
hifiveはMVC型のアーキテクチャを採用しています。
hifiveではクライアントの構成要素を大きく
- ビュー:画面(HTML文字列)の作成
- コントローラ:DOM操作、イベントハンドリング、ロジックの呼び出し
- ロジック:通信、DB操作、計算
の3つのレイヤーに分類し、それぞれがどのような処理を行うかを定めています。
これにより、処理内容ごとに記述箇所や構造が一定になり、どこにどのようなパターンで処理を書くかで
悩む必要がなくなると同時に、特に多人数開発においてレビュー・引き継ぎ・保守などの際に
どこを見ればよいかがわかりやすくなります。つまり、保守性、可読性を高めることができます。
また、各レイヤーでは多くの場面で必要となる
- ビューテンプレートエンジン
- UIブロック
- 非同期処理ライブラリ
などの機能・仕掛けがあらかじめ組み込まれており、
個別の記述量を減らし、コードをクリーンに保つことができます。
ver.1.1現在の主要設計要素とそれぞれの役割は以下の通りです。
コントローラ
主にイベントハンドリングとビュー操作を行います。コントローラは階層的に定義することができます。
画面を機能ごとに分割しその構造と対応させて記述することで1つのコントローラの責務が明確になり、
コードを小さなまとまりに保つことができます。
これにより、多人数での並列開発のしやすさや保守時の変更点の明確化につながります。
hifiveのコントローラは階層的に定義できることが一つの特徴です。
機能単位にコントローラを分割して記述することで、
1つ1つのコントローラのコード量を小さく保ち、メンテナンス性を向上させることができます。
また、コントローラごとに対応(担当)する画面範囲を分けることができます。
これによってその範囲外の要素へのアクセス・イベントハンドリングを行わないようにすることができます。
複数のコントローラが1画面中に存在するような複雑な画面でも
意図しない動作の発生を避けられます。
ビュー
ビューはHTMLおよびCSSで記述されます。コントローラによる明示的な操作、
または後述のデータバインド機構によって画面が更新されます。
hifiveでは、用途に応じて以下3種類のビュー操作方式を提供しています。
- jQuery(通常のDOM操作)
- 特定の要素(またはその集合)を操作する場合に使用します。
コントローラは$find()メソッドを持っており、この関数を使用すると、
そのコントローラがバインドされている要素の子孫から、与えられたセレクタにマッチする要素を持つjQueryオブジェクトを取得できます。
これにより、jQueryを直接使った場合と異なり、コントローラが操作する要素をコントローラ配下の要素に限ることができ、
誤って関係ない要素を操作してしまうことを防ぎます。
- 特定の要素(またはその集合)を操作する場合に使用します。
- テンプレートエンジン
- 複雑なタグ構造を持つビューを生成する場合に使用します。
jQuery単体でビューを生成すると、最終的に生成されるビューのタグ構造がわかりにくくなります。
また、例えば計算ロジックの途中でビューを生成すると、保守時にビューがどこで操作されているかがわかりにくく、影響範囲が絞れなくなってしまいます。
そのため、ビューの生成をテンプレートエンジンに任せることで、タグ構造がわかりやすく、保守時の変更箇所もわかりやすくできます。
hifiveでは、JSPライクに記述できるテンプレートエンジン、EJSを採用しています。
- 複雑なタグ構造を持つビューを生成する場合に使用します。
- データバインド (Introduced in ver.1.1)
- データバインドは、画面の更新が頻繁に行われる場合に利用を検討すべき仕組みです。
この仕組みでは、ビュー側に「この要素の値はこのデータオブジェクトのこのプロパティの値と関連付けられている」は、「DataItem(データアイテム、データモデルの各レコードに相当)」やその一時的利用形態である「ObservableItem」の変更イベントを検知し、関連付けされているビューを自動的に更新します。
従って、データの変更を画面に反映させる際jQuery等でDOM操作を行う必要がなく、プログラマはデータ操作に専念することができ、ビューとロジックの分離が促進されます。
また、データバインドでは、1つのデータオブジェクトの変更を同時に複数の要素に反映させることが可能です。これにより、画面上に複数のコンポーネントが存在する場合に、画面全体の整合性を保つことが容易になります。
- データバインドは、画面の更新が頻繁に行われる場合に利用を検討すべき仕組みです。
ロジック
hifiveのLogicは「処理」、たとえば
- 複雑な計算
- サーバとの通信
- ローカルDBとのやり取り
などを行うものと定めています。(「画面操作」や「イベントハンドラ」などは行ってはいけません。)
これらの「処理」だけを書くように気をつけることで
- 可読性の向上 → 処理を追いやすくなる
- 再利用性の向上 → 画面に依存しない
- テスト容易性の向上 → 単体テストツールでのテストを行い易くなる
- 保守性の向上 → 見た目を変える時に処理のJavaScriptコードに手を入れなくて済む。
Logicを修正する際画面の具体的な実装(HTMLの構造等)に依存しない
などのメリットが生まれます。
ロジックは、計算処理・サーバー通信などを記述する層です。
特に、複数の画面(コントローラ)で共通的に使われる処理をこの層で記述します。
JavaScriptをブラウザで実行した際の性質上Logicでの画面操作はできてしまいますが、
それを行わないよう気を付けることでコードの使いまわしがしやすくなり、単体テストも記述しやすくなります。
HTML5技術を使ったアプリケーションでは様々なクライアントAPIを使用するため、
アプリケーションによってはクライアント側でも十分な単体テスト・カバレッジが求められる場合があります。
その際のテスト容易性のためにも、複雑なロジックは別にしておくことをおすすめします。
データモデル
データモデルは、事前に与えられたスキーマに基づいて型・制約のチェックを行うことができるデータ管理レイヤです。
JavaScriptには型の概念がないためオブジェクトの任意のキーに任意の値をセットすることができてしまいますが、
実装中あるいは保守時にデータの構造が変わった場合にバグの発見が困難になってしまいます。
そこでhifiveのデータモデル機構では、(多くのDBMSがそうであるように)予めモデルのスキーマを定義するようにしています。
そして、オブジェクト(RDBMSの「レコード」に相当)に対する値の出し入れをget()・set()メソッドで行うこととし、
set()の際にスキーマに記述されていないキーへの代入をエラーにするようになっています。
スキーマには型だけでなく制約(「1以上10未満」「空文字でない」など)も記述できます。
また、モデルには「依存計算プロパティ」を定義することも可能です。
例えばプロパティXを「プロパティSに依存する依存計算プロパティ」であるとした場合、
Sが変更されると自動的にXを計算するための関数が呼ばれます。
その関数の戻り値が新たなXの値となり、再度Sが変更されるまでその値はフレームワークによって保存されます。
さらにhifiveのデータモデル機構は、データオブジェクトの追加・削除や変更のタイミングを外部に通知するイベント機構も備えています。
hifiveのビュー機構の一つであるデータバインド機構はこれに対応しており、
データモデルを操作するだけで自動的に画面に反映させることができます。
リスト等繰り返しデータの表示に特に威力を発揮し、開発者はコードの記述に専念することができるようになります。
コントローラ/ロジックに対するAOP(アスペクト)
hifiveのControllerとLogicには、AOPの概念が取り入れられています。
具体的には、Controller/Logicの各メソッドの呼び出し前にインターセプタ(メソッドのフック)をセットすることができます。
イベントハンドラ等のロギング、処理時間計測等を一括で仕掛けることができます。
パフォーマンス系の問題は(残念ながら)開発の中~終盤に発覚することが多く、問題がありそうな関数の中に
doSomething();
var end = new Date();
alert(end - start);
のような時間計測コードを書いて回ることになりがちです。
このような場合にAOPの仕組みを使うことで、コードを書き換えずに時間計測することが可能になります。