Angular.jsで組む場合のアーキテクチャは、MVCじゃなくてMVVMの方が良いっぽいと思った話

Angular.jsを何度か仕事で使ってみて、Angular.jsを使う場合のアーキテクチャMVCじゃなくてMVVMにしたほうが良いなと思った話を書く。

Angular.jsをMVCフレームワークだと勘違いしていた

少しAngular.jsについて今まで勘違いしていたことがあって、Angular.jsではコントローラを定義できるのでてっきりMVCアーキテクチャで作るものとばかり思っていた。

公式ウェブサイトのタイトルをよくよく見てみると、「Superheroic JavaScript MVW Framework」と書いてある。MVWのWってなんだよとか思ってたらWhateverの略で、要するにMVCでもMVVMでもなんでも良いということらしい。

MVCで組んで困ったこと

勘違いが解ける前は、普通にMVCフレームワークとしてAngular.jsを使っていたけどもそれで何が困ったかというと、コントローラのコードが肥大して困った。

MVCでは、ビジネスロジックを扱うものをモデルとして、UI側のコードをビューとして、そしてモデルとビューを寄せ集めてくっつけるコントローラという風にアプリケーションのコードを3つに分割する。

それをAngular.jsで実際にやってみると、ビュー側で必要とする値とモデル側が管理する値が違ってくることが多く、その差異を埋め合わせるためのコードがコントローラでどんどん増えていって見通しが悪いものになりがちだった。

この傾向は、複雑なフォームやコンテキストに応じて変化するUIなど、インターフェイスがリッチになればなるほど強くなった。というのもリッチになればなるほどビュー側が欲しい値とモデル側が提供するデータの差異がさらに激しくなるからである。

MVVMって何よ

ModelとViewとViewModelに分けるアーキテクチャで、MVCのコントローラの代わりにビューモデルを用いる。ビューモデルとは、ビューを扱うためのモデルで、ビジネスロジックのために設計されたモデルと、実際に人間が触れるインターフェイス側のビューとの違いを吸収する。ビューモデルとビューとの通信は、通常データバインディングを用いて自動的に行われる。詳しくは、WikipediaMVVMの記事を参照する。

ビューモデルでモデルとビューとの間を吸収するって、それってコントローラで記述するのと何が違うんだという話になるかもしれないが、ビューモデルはモデルと同様にクラスを書いてユニットテストを書きやすい状態で宣言する。クラスとして宣言することで再利用やモジュール化もしやすくなる。ユニットテストが書きづらく取り回しも効きづらいコントローラでアドホック的にモデルとビューの違いを吸収するよりかは、予めきちんと定義したビューモデルを作ってユニットテストを書きやすい状態にしておくほうがより良いと思う。

ビューモデルを定義したら、コントローラでは$scopeに対してそのビューモデルのインスタンスを割り当てるだけで終わり、というような状態にする。擬似コードだと次のような感じ。

angular.module('app').controller('FoobarController', function($scope, FoobarViewModel) {
    $scope.foobar = new FoobarViewModel();
});

まとめ

  • Angular.js自体は、MVCでもMVVMでもどちらでも大丈夫
  • MVCで組むと、リッチUIの時にコントローラが肥大しがち
  • そういう時には、MVVMが使える