読者です 読者をやめる 読者になる 読者になる

cycle.jsでのMVI分割時にどう考えてるか

実装していくうちにレイヤやシステムの境界に対する認識が変わってきたので現状を整理。

Intent

アプリ外部から受け取った入力をアプリ内部で使える語彙・データ形式に変換するためのアダプタ。アプリ外部からの入力を解釈して、アプリに何をしてほしくてそれを入力したのかを判断し、その要求をModelに伝える。「xxxが起こった」という形式ではあまり良くなくて、「xxxしてほしい」という形式になるのがベストという理解をしている。

  • 例: ボタンがクリックされたので記事を作成してほしい (DOM経由の入力に反応する例)
  • 例: 記事が作成されたので記事を表示するページに遷移してほしい (HTTP経由の入力に反応する例)

Model

Intentからの入力をもとに状態の管理を行う。状態は変数に格納するとかではなくて、Observableの中に生き続ける。Elixir/Erlangやったことがある人は、Agentを使って無限ループするプロセスでKVSを表現したりする事例を知っているかもしれないけど、そういう感じに近いです。状態が正規化されていない場合は、状態同士の依存関係などもModelで表現される。例えば記事ID一覧と記事一覧を状態として保持しているとしたら、「記事一覧は記事ID一覧に依存しており、記事ID一覧が変更されるとそれに伴って記事一覧も更新される」といったことがModelで表現されることになる。

View

Modelからの入力をもとにアプリ外部へリクエストを返す。DOMに対しては描画リクエスト、History (ブラウザの履歴管理の抽象) に対してはpushStateリクエスト、HTTPに対してはHTTPリクエストを送るといったイメージ。

Driver

Drievrは、アプリ外部とのインターフェースとなるやつ。ビジネスロジックを一切含まない、ので普通はライブラリ化して別のアプリと共有できる。公式ではHTTP DriverやDOM Driverがある。Modelでアプリ外部と通信するのは得策ではない。例えばIntentから「記事を削除してくれ」という入力がきたらModelから記事を削除するHTTPリクエストを送りたくなるだろうが、「HTTPリクエストを送信する要求」をViewに返すのがよい。メリットを理解しにくければ、実際にシステム外部と通信しないほうがテストしやすいということを考えるととりあえず納得しやすい。で、「HTTPリクエストを送信する要求」をもとに実際にリクエストを送ってレスポンスを受け取るのがDriverの役割。