Cycle.js の MODEL-VIEW-INTENT のチュートリアルから MVI (Model-View-Intent) と MVC (Model-View-Controller) に関する説明を抜粋して翻訳しました。原文のライセンスは MIT です。
「Passive/Reactive プログラミングの違いを学ぶ」 をあらかじめ読んでおくと理解が深まるでしょう。筆者の André Staltz さんは Flux、Redux などのアーキテクチャの比較や評価を UNIDIRECTIONAL USER INTERFACE ARCHITECTURES と NOTHING NEW IN REACT AND FLUX EXCEPT ONE THING の記事で述べています。
MVI (Model-View-Intent) について
MVI は Cycle.js の main()
関数を3つの部分: Intent (ユーザーをリスニングする)、Model (情報を処理する)、View (ユーザーに出力を戻す) にリファクタリングするシンプルなパターンです。
function main({DOM}) {
return {DOM: view(model(intent(DOM)))};
}
-
intent()
関数- 目的: DOM イベントをユーザーの意図として解釈する
- 入力: DOM ドライバーソース
- 出力: Action Observables
-
model()
関数 - 目的: 状態を管理する
- 入力: Action Observables
- 出力: state$ Observable
-
view()
関数 - 目的: Model からの状態を視覚的に表現する
- 入力: state$ Observable
- 出力: DOM ドライバーシンクとしての VTree の Observable
Model-View-Intent はアーキテクチャなのでしょうか?新しいアーキテクチャなのでしょうか?そうだとすると、Model-View-Controller とはどのように異なるのでしょうか?
MVC が本当に意図すること
Model-View-Controller (MVC) はユーザーインターフェイスのためのアーキテクチャの礎(いしずえ)として1980年代から存在してきました。MVVM や MVP などほかの複数の重要なアーキテクチャは MVC にインスパイアされています。
MVC は Controller によって特徴づけられます。Controller はほかの部分を操作し、ユーザーがとる何らかのアクションに合わせて、更新します。
MVC の Controller は Reactive の理想とは両立しません。Controller が Proactive コンポーネントだからです (passive Model もしくは passive View も含みます)。しかしながら、MVC のオリジナルのアイディアは2つの世界: コンピューターのデジタル領域とユーザーのメンタルモデルのあいだの情報を変換するための方法でした。Trygve 自身の言葉を引用します。
MVC の主要な目的は人間のユーザーのメンタルモデルとコンピューターのなかにあるデジタルモデルのあいだの隔たりを橋渡しすることです。
– Trygve Reenskaug、MVC の作者
proactive な Controller を避けることで、 MVC の理想を保ち続けることができます。実際、view()
関数を監視する場合、状態 (コンピューターのデジタルモデル) をユーザーに役立つ視覚的な表現に変換する以外のことは行いません。View はある言語から別の言語、バイナリデータから英語および人間が扱いやすいほかの言語への変換です。
反対の方向はユーザーのアクションから新しいデジタルデータへのストレートな変換になります。これが intent()
が行うことです。デジタルモデルのコンテキストでユーザーが影響を及ぼそうとすることを解釈することです。
Model-View-Intent (MVI) は reactive であり、functional で、MVC の核となるアイディアに従います。Intent は User を監視し、Model は Intent を監視し、View は Model を監視し、User は View を監視するので、MVI は reactive です。functional であるのはそれぞれのコンポーネントが Observable に対して参照透過性のある関数で表現されるからです。これはオリジナルの MVC の目的に従います。View と Intent はユーザーとデジタルモデルのあいだの隔たりを一方向で橋渡しするからです。
DOM イベントの問い合わせに CSS セレクターなのか?
プログラマーのなかには DOM.select(selector).events(eventType) が悪い習慣
ではないかという懸念を抱く人がいます。jQuery をベースとする
プログラムでのスパゲッティコードに似ているからです。
彼らは onClick={this.handleClick()} のような
イベントに対するハンドラーコールバックを指定する視覚的
な DOM 要素を好みます。
Cycle DOM でのセレクターをベースとするイベントの問い合わせ
の方法は熟慮のもとに選ばれました。この戦略は Reactive な
MVI を機能させます。開放/閉鎖原則にインスパイアされました。
reactivity と MVI の重要性。
View が onClick={this.handleClick()} をもつとすると、
View はデジタルからユーザーのメンタルモデルへのシンプルな変換
ではなくなります。ユーザーのアクションの結果として起きることも
指定しているからです。
Cycle.js アプリのすべての部分を reactive に保つため、
Model の視覚的な表現をシンプルに宣言する View が必要です。
さもなければ View は Proactive なコンポーネントになります。
状態が視覚的に表現される方法を宣言することに View の責務を限定
するために役立ちます。単一の責務をもち、UI デザイナーに親しみや
すいで。これはオリジナルの MVC の View のコンセプトにも沿いま
す。
「ビューはマウスのオペレーションやキーストロークのような、ユーザーの入力は知ってはならない。」
ユーザーのアクションを追加することは View に影響を与えません。
要素から新しい種類の部院とを得るために Intent のコードを修正
する場合、要素のなかのコードを修正する必要はありません。View
は修正されず、またそうあるべきです。状態から DOM への翻訳は変化していないからです。
Cycle DOM による MVI の戦略は View の大半の要素にセマンティックなクラス名をつけることです。
そうすれば、それらのどれがイベントハンドラーをもつのか悩む必要はありません。
クラス名は View (DOM シンク) と Intent (DOM ソース) が同じ要素を参照のために使う共通のアーティファクトです。
コンポーネントの章で見るように、Cycle.js では、`isolate` ヘルパーのおかげで、グローバルなクラス名の衝突のリスクは問題になりません。
MVI はアーキテクチャですが、Cycle において、main()
の関数を分割したものにすぎません。
実際、MVI 自身は main()
を複数の関数にリファクタリングすることで自然にあらわれたものです。このことは Model、View、と Intent はコードを設置するための厳密なコンテナーではないということです。そうではなく、これらはコードを組織化する便利な方法であり、これらは単なる関数なので、かんたんにつくることができます。利便性があれば、大きすぎる関数は複数に分割します。MVI はコードを組織化するためのガイドとして使います。しかし、意味をなさないのであれば、コードを MVI の制限のもとに押し込めないでください。
これは Cycle.js が sliceable であるということを意味します。
MVI は main()
をスライスする方法の1つにすぎないのでしょうか。
"Sliceable"?
周辺部分を大きく修正することなく、
コードの一部を抽出することで、プログラムをリファクタリングできることを意味します。
Sliceability は関数型のプログラミング言語、
S 式を使ってコードをデータとして扱う Clojure のような
LISP をベースとする言語でよく見られる機能です。