アイディアやツールは比較をすることでより理解が深まります。
この記事では、日々のウェブアプリケーション開発で行っていることを一覧し、BackboneとAngularがそれぞれどのように役に立つのかを示します。
どのようなことを解決したいのか
ウェブ開発者としてやらなければならないことは、ほとんど次に挙げるカテゴリに分類されます。
-
ビジネスロジックを実装する
-
DOMを構築する
-
ビューのロジックを実装する(宣言的かつ命令的)
-
モデルとビューをすりあわせる
-
UIのインタラクションの複雑さを管理する
-
状態とルーティングの管理
-
コンポーネントの作成と組み込み
ほとんどのクライアントサイドフレームワークはこれらの課題に対処します。
Backbone
Backboneがどのように課題を解決してくれるのか見てみましょう。
ビジネスロジック |
Backbone ModelとCollection |
DOMの構築 |
Handlebars |
宣言的なビューロジック |
Backbone View |
命令的なビューロジック |
Backbone View |
ビューとモデルの同期 |
StickIt |
UIのインタラクションの管理 |
JS ObjectsまたはMarionette Controllers |
状態とルーティングの管理 |
Backbone.Router |
コンポーネントの作成と組み込み |
手動で行う |
For visual people:
Backboneについて言うなら...
BackboneとAngularを比較するのは公平ではありません。なので、この記事ではBackboneはBackbone + Marionette + アドインを示すことにします。
ビジネスロジック
アプリケーションの巨大なビジネスロジックの固まりはBackboneのモデルとコレクションになります。これらのオブジェクトはバックエンドのリソースに対応します。このモデルとコレクションでビューをバックアップします。
Backbone.ModelとBackbone.Collectionを拡張しなければならないので少し複雑になります。
まず、すべてのドメインオブジェクトをPOJOとBackboneモデルに分離します。POJOは普通、テンプレートの描画とサーバとの通信に使われます。Backboneモデルはオブザーバルプロパティが必要なときに使われます(例えば、データバインドをセットアップ)。
また、Backboneはオブザービングファンクションをサポートしないので、すべてのプロパティはソースとなるプロパティが変わるとリセットされます。これによって複雑さが増します。読みにくくテストしにくいコードになってしまいます。また、すべての依存物は明示的に定義されなければなりません("change:sourceProperty, this.recalculateComputedProperty)。
DOMを構築する
Backboneはテンプレートエンジンを使ってDOMを構築します。理論的には、好きなエンジンを使えます。しかし、実際に大規模なアプリケーションで使われるのは、MustacheとHandlebarsです。その結果、Backboneのテンプレートはロジックが少なくなり、文字列ベースになってしまいます。しかし、そのようにする必然性はありません。
ビューロジック
ビューロジックを宣言的と命令的に分けるというアイディアは古い(オリジナルのMVCパターンまでさかのぼります)です。イベントハンドリングの構成とデータのバインディングは宣言的です。イベントハンドリングそのものは命令的です。Backboneはこのふたつを明確に区別しません。Backbone.Viewに混じっています。
モデルとビューの同期
Backboneは最小限の機能しか提供しないので、データバインディングを組み込みでサポートしません。小さなプロジェクトでは問題ありません。ビューにモデルとDOMの同期を任せればいいのですから。しかし、アプリケーションが成長するに従って、管理できなくなります。
いくつかのアドオン(Backbone.StickItのような)を使ってビューとモデルの同期に関わる負荷を低減できます。モデルとビューの同期のような些細な問題よりも、複雑なインタラクションを管理することに注力できます。これらのアドオンのほとんどは普通のJavaScriptを使って構成できるので、アプリケーションに合うかたちで抽象して設定することができます。
Backboneのデータバインディングの欠点はオブザーバルプロパティに依存しているにも関わらず、テンプレートエンジンはPOJOを使うことです。DOMをこのふたつの方法で扱うのはコードが重複することになります。
複雑なUIインタラクションを管理する
すべてのUIインタラクションは単純なもの(Observer Synchronizationを使って管理する)と複雑なもの(Flow Synchronizationが必要)に分けられます。
上述したように、単純なインタラクションはBackbone.Viewでデータバインディングとイベントハンドラを使って処理できます。Backboneは複雑なUIインタラクションを制御するためのソリューションを持っていませんので、自分のアプリケーションに一番合うものを選ぶ必要があります。Backbone.Viewを使う人もいますが、私のおすすめは反対です。Backbone.Viewは機能が大きすぎるのです。Supervising Presenterは私が複雑なインタラクションを管理するときによく使うパターンです。
状態とルーティングの管理
Backboneはとてもシンプルなルータの実装を提供します。ビューの管理とアプリケーションの状態の管理のための仕組みは提供しません。すべて手動で行う必要があります。それゆえ、実際にはほかのライブラリ(router.jsなど)が必要になります。
コンポーネントの作成と組み込み
Backboneでは、自由にコンポーネントを作成し組み込むことができます。繰り返し同じコードを書かなければならず、さらに、コードをしっかりと構成するために守る必要のある原則も多いです。
Angular
同じ課題をAngularではどのように解決するか見てみましょう。
ビジネスロジック |
JSオブジェクト |
DOMの構築 |
指示的 |
宣言的ビューロジック |
指示的 |
命令的ビューロジック |
コントローラ |
ビューとモデルの同期 |
組み込みの仕組み |
UIインタラクションの管理 |
コントローラ |
状態とルーティングの管理 |
AngularUI Router |
コンポーネントの作成と組み込み |
依存性注入 |
For visual people:
ビジネスロジック
Angularはオブザーバルプロパティを使いませんので、モデルの実装には制約はありません。拡張する必要のあるクラスや従う必要のあるインターフェイスもありません。やりたいようにできます。実際には、多くの開発者はPOJOを使います。次のような利点があるからです。
- すべてのドメインオブジェクトがフレームワークに依存しないので、アプリケーションをまたいで再利用できます。
- データに近いので、クライアントとサーバの間の通信をシンプルにします。
- ビューを描画するのに使えので、toJSONを実装する必要はありません。
- コンピュートプロパティは関数としてモデル化されています。
テンプレートとビュー
Angularのテンプレートはコンパイルされる前のDOMの一部です。コンパイルの間、AngularはDOMのサブツリーを変換し、JavaScriptをアタッチします。コンパイルの結果は別のDOMのサブツリーになり、これがビューになります。言い換えれば、ビューそのものを作成する必要はないのです。Angularはテンプレートをコンパイルすることでビューを作成します。
DOMを構築する
BackboneはDOMの構築とビューロジックを明確に分離します。Angularの場合、このふたつを別けていません。DOMの構築にもビューの振る舞いを定義するのにも同じ仕組みを使います。
ビューロジック
Angularは宣言的と命令的なビューロジックを明確に別けます。宣言的なロジックはビューによって実現され、命令的なロジックはコントローラで実現されます。
この分離は恣意的に見えます。しかし、とても重要です。
まず、何を単体テストの対象にするべきかを特定できます。テンプレート内でエンコードされた宣言的なロジックはテストする必要はありません。コントローラのためにテストを書くのは良いアイディアです。
また、ビューからコントローラまで、すべての依存物は同じ方向に向かいます。コントローラはビューもDOMも意識しません。なので、コードを再利用し、テストも簡単にできます。
モデルとビューの同期
Angularはデータバインディングを組み込みでサポートします。ほとんどのクライアントサイドフレームワークと比べ、Angularはオブザーバルプロパティに依存しません。そのかわり、ダーティチェックを行います。
Angularのダーティチェックはいくつかの優れた属性を持っています。
- モデルは監視されているということを意識しません。
- オブザーバルプロパティの間の依存関係を定義する必要はありません。
- 関数もオブザーバルです。
しかし、欠点もあります。
- サードパーティのコンポーネントやライブラリと統合するとき、Angularがモデルに行われた変更をわかるようにする必要があります。
- 状況によっては性能に悪影響が出ます。
複雑なUIインタラクションを管理する
上述したように、コントローラはUI要素の宣言的なロジックを実装することに責任を持ちます。コントローラをSupervising Presenterとして使い、複雑なUIのインタラクションを管理することもできます。
状態とルーティングの管理
Backboneと同様に、Angularの組み込みルータはとてもシンプルですが、現実のアプリケーションの実装には不十分です。ありがたいことに、AngularUI Routerプロジェクトがあります。これは、アプリケーションの状態、ビューを管理し、ネストをサポートします。つまり、ルータに期待することはすべてできます。また、Backboneと同様、ほかのライブラリ(例えばrouter.js)も利用できます。
コンポーネントの作成と組み込み
AngularにはIoCコンテナがあり、依存性注入と同様に、モジュール化されたコードを書くことを強制します。これによって、再利用しやすくなり、テストしやすくなり、繰り返し同じことをする必要もなくなります。欠点は、複雑になりやすいこと、コンポーネントを制御しにくくなることです。
おわりに
BackboneとAngularがどのようにウェブアプリケーション開発の課題を解決するのかを簡単に見てきました。このふたつのフレームワークは異なる解決策を持っています。Backboneはテンプレートの描画、データバインディングの準備、コンポーネントの組み込みについて多くの選択肢を提供します。Angularはこれらの問題に対して定型的なソリューションを提供しますが、モデルやコントローラを構築する場合は柔軟です。
著者について
Victor SavkinはNulogyのソフトウエアエンジニア。関数型言語、ウェブプラットフォーム、ドメイン駆動設計に関心を持っている。JavaScriptを使って大規模なアプリケーションを作っている。言語オタクになるため、Smalltalk、JS、Dart、Scala、Haskell、Clojure、Iokeで遊ぶのに時間を費やしている。ブログではvictorsavkin.comRubyとJSで大規模なアプリケーションを作成することについて書いている。Twitterアカウントは@victorsavkin。