Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save okunokentaro/826d99731305ed1ac853ee5ee50c8961 to your computer and use it in GitHub Desktop.
Save okunokentaro/826d99731305ed1ac853ee5ee50c8961 to your computer and use it in GitHub Desktop.
Waltsは何を提供するのか
2016/10/04 にQiitaに投稿した記事のアーカイブです
---
@armorik83です。Angular用ライブラリ"[Walts](https://github.com/crescware/walts)"を公開したところ、想像以上にフィードバックを数多く頂いたので、その中で疑問に思われがちな点、補足できていなかった点などをこの記事で紹介します。
# 関連記事
- 拙記事
- [どうしてWaltsを開発したのか - そして昨今のFlux](http://qiita.com/armorik83/items/2c7933d3376c51c01461)
- [Walts - Angular 2向けFluxライブラリを作った](http://qiita.com/armorik83/items/191d50cbf093778198fe)
- Waltsを使ってくださった方による記事
- [Angular2系向けFluxライブラリのWaltsを使ってみた](http://qiita.com/sisisin/items/4004591b031bbe7a22a2)
# Waltsは何か
これまでに書いた記事はポエム調だったため、改めて具体的にWaltsは何であるか紹介します。
- [Walts](https://github.com/crescware/walts)は[Angular](https://angular.io/)(>=2.0.0)向けのライブラリです
- [RxJS](https://github.com/ReactiveX/rxjs)を用いており[Flux](https://facebook.github.io/flux/)アーキテクチャや[Redux](http://redux.js.org/)に影響を受けて開発したものです
- Angularアプリケーション開発において状態管理に一定の秩序を提供します
- アプリケーション内で常に1つの`State`を扱います
- `State`への書き込み処理は全て`Actions`へ、`State`からの読み込み処理は全て`Store`へ記述します
# Waltsの必然性
## MVCは悪か
現代のフロントエンドでは、MVCは古く悪いものでFluxを採用しなければいけないかというと、そうではありません。(ここでいうMVCはAngular 1時代にあったような構造という程度に解釈してください)
MVCでは ― 特にAngular 1.xのコンテキストで話すと ― いくつものシングルトンServiceを常駐させ、複数のComponentがそのServiceを呼び合い値を格納し合うことで、状態の管理と受け渡しを行っていました。これは一人、または少人数で小規模のアプリケーションを開発する場合には問題にならないかもしれませんが、大規模なアプリケーションを開発していく上で混沌をもたらします。
シングルトンはグローバル名前空間にひとつ常駐するインスタンスなので、シングルトンが持つプロパティは全てグローバル変数のような影響範囲を持っています。これを複数のComponentやServiceが書き換え合ったとき、いつ誰が書き換えるか把握しづらくなることから、アプリケーションの状態遷移は追いにくいものとなり予期せぬ値の書き換えがバグに繋がります。
Angular 1.xでは`$scope.$broadcast()`と`$scope.$on()`を用いたイベント駆動を採用することで、複数のServiceが入り乱れる状況を回避する方法が紹介されました。Angular 2.0ではこれらのAPIが廃止された代わりに、同等以上の機能を提供するRxJSが採用されました。
## Fluxは善か
では、Fluxを採用すればすべて解決するかといえば、それは開発するアプリケーションの規模によるとしかいえません。
Fluxは「**値の受け渡しは一方向である**」ことと「**変更された値は取りに行かず値がイベントとして送られてくることを待つ**」ことが特徴です。これは、複数のServiceが任意のタイミングでプロパティを書き換える状況を「値の受け渡しは一方向である」という制約によって整え、「変更された値は取りに行かず値がイベントとして送られてくることを待つ」ことでComponentは値の変更の瞬間を知る必要がなくなり、ただ宣言的にテンプレートを記述するだけでよくなります。
MVCが悪、Fluxが善という話ではなく、開発フローに意図的な制約を設けることで長期的に保守しやすいアプリケーションを構築したい、という話なのです。そのため、Fluxの採用がかえって大げさになる場面も存在し、保守しやすい状態を続けられるのであればFluxの採用が必須というわけではありません。
## WaltsはAngularアプリケーション開発の何を助けるか
前節で述べたことと同様に、Waltsは小規模のアプリケーションを開発する上では大げさかもしれません。逆に、大きくなるアプリケーションの開発ではWaltsが助けになるように提供しています。
WaltsはFluxと同じ思想なので「**値の受け渡しは一方向である**」ことと「**変更された値は取りに行かず値がイベントとして送られてくることを待つ**」ことをアプリケーション内に強制させます。更に「**`State`は常にひとつである**」ことと「**書き込み処理はActionsに、読み込み処理はStoreに書く**」ことを制約として追加しています。
これらの制約は全て、アプリケーションを保守しやすい状態に維持することを目的としていますが、特にチーム開発でのケースを想定しています。
設計レビューやコードレビューでは、往々にして「この処理はどこに書くか」という話題が上ります。そしてよくあるのが「なんとなくここに書いた」「一連の処理だからここに書いた」というものです。これは肥大化するアプリケーションにとっては危険な判断で、数万行から十数万行を超えるアプリケーションになる頃にはこの状況は決壊します。レビューはそのために存在するのですが、ここはお互いの好みを押し付けあう場ではありません。お互いの好みではなく、信頼できる先人のノウハウ、客観的根拠をもとに懸念を正していく場です。
Waltsは、既に成功している先人のノウハウFlux, Reduxと、客観的根拠であるCQRSアーキテクチャ(コマンドクエリ責務分離)からヒントを得ています。CQRSはアーキテクチャレベルで副作用をうまく扱うための方法論です。書き込み処理(コマンド)は副作用が伴うが、読み込み処理(クエリ)は現在の値を返す、もしくは関数を通して返すため副作用が伴わないという点から、これらコマンド・クエリを一連のものとして記述せず分離させて記述することで、それぞれのテスト性を高めるという目的があります。Waltsが書き込み処理と読み込み処理をActions, Storeに分けて記述することを推奨するのは、こういった背景によるものです。
これらの根拠を備えておくことで、レビュー時にはお互いが納得できるコードレビューを行いやすくなります。長文かつ保守されないような社内ドキュメントよりも遥かに客観的です。Angularでは状態管理についてのプラクティスは何も提供していないので、Waltsを併用することでこれらの指針を享受できることを目的としています。
## Dispatcherによる分離
コマンドとクエリの分離はDispatcherによって行われます。ActionsとStoreを繋ぐのがこのDispatcherです。Dispatcherが持つ機能をすべてStoreに実装して提供してしまえば、皆さんは覚えるAPIが一つ減ったことでしょう。しかし、これではイベントの発火にもStoreを使い、値のsubscribeにもStoreを使うことになってしまいます。
AngularはComponentにServiceをDIして規模を広げていくフレームワークですが、どのComponentに何のServiceがinjectされているか、もっと神経質になるべきです。ここの数が多い場合、そのComponentは何かしらの責務過多となっており不健康である可能性があります。Componentのconstructor引数はComponentの役割と健康度を明示するバロメータとなるのです。
WaltsのStoreをinjectしていた場合、そのComponentでは「アプリケーションのStateを受け取っている」ことが明示されます。一方同様に、ActionsとDispatcherをDIしていた場合、そのComponentでは「何かしらのユーザの操作から状態変更を行っている」ことが明示されます。
もしStore, Actions, Dispatcherの3つをDIしていた場合は、中枢的な機能を有していると見ることができます。なお、この状況は常に行うべきではありませんが、Root Componentなどではあり得ると認識しています。
## TypeScriptであるということ
Angularは2.0よりTypeScript推奨のフレームワークとなりました。ES2015系でも書ける、とは言いますが事実上TypeScriptが前提です。Waltsも同様にTypeScriptによって開発されていますが、ユーザが利用する際の型定義の記述性などを考慮して設計しています。
WaltsのActionsには、関数そのものを記述します。`eventType`, `payload`から成るオブジェクトの宣言ではありません。ここがReduxと大きく異なります。関数はActions内で同期的に実行されるのではなくDispatcherを経由しStore内で実行されるため、実行されるレイヤーでいえばReduxと変わりないのですが、記述場所が変わるだけで異質に見えるかもしれません。
こうした意図は、いかにTypeScriptの型を書きやすくできるかにあります。`eventType`, `payload`から成るオブジェクトをイベントに載せた場合、受け手では必ず「`eventType`は何か」というswitch分岐が登場し、あわせてTypeScriptでは「この`eventType`ならば`payload`は`T`という型である」と宣言せねばなりません。Reducer内に記述する処理も型推論ができませんから、型定義とReducerにおいて二重の型記述が必要となってしまいます。
Waltsではこの問題を、Actionsに記述することで一箇所で済むようにしました。イベント名を頼りに処理を追わなくてもよいため、IDEでの扱いやすさにも繋がります。
## Waltsは無くてもいいのでは
よく指摘されるのでここで説明しておきます。まず、Angularアプリケーションを開発する上でWaltsの利用は**必須ではありません**。そのため、Waltsが無くてもAngularアプリケーションは構築できるからWaltsは不要である、という意見は否定しません。
ここまでの内容を問題と思わなければ、もしくはAngularにFluxは不要と感じるならば、Waltsを使う必要はありません。
# Waltsは秩序を提供します
前章の通り、Waltsは、信頼される客観的根拠に基づいた規則を採り入れることで、秩序を提供します。ただしAngularアプリケーション開発に不自由さをもたらす意図は無いため、Waltsを使いながらこれらの規則を守らない、という自由な記述も可能です。ここはあくまでもサポートするという名目なので、最終的な判断は開発者に委ねられます。ただし自由にしすぎたが故に、規模拡大と共に破綻するリスクが増すこともお忘れなく。
# Waltsの今後の予定
数多くの貴重なフィードバックをありがとうございます。また、私の知らない別のライブラリも紹介してもらえました。これらをヒントにいくつかの修正と機能追加を予定していますが、大枠としては安定させる方向に努めます。
今後は英語ドキュメントの整備、APIドキュメントの整備、日本語チュートリアル記事の整備を予定しています。優先度は界隈の流れを見ながら判断しますが、早い段階でチュートリアルを提供できることを望んでいます。
もしこの記事をご覧になってWaltsに興味が沸かれたのであれば、ぜひお試しください。
```
npm install --save walts
```
[https://github.com/crescware/walts](https://github.com/crescware/walts)リポジトリにはサンプル・アプリケーションの例がいくつか掲載されています。
---
Waltsをどうぞよろしくお願いいたします。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment