📐

Waltsは何を提供するのか

2021/07/04に公開

@armorik83 です。Angular 用ライブラリ"Walts"を公開したところ、想像以上にフィードバックを数多く頂いたので、その中で疑問に思われがちな点、補足できていなかった点などをこの記事で紹介します。

関連記事

Walts は何か

これまでに書いた記事はポエム調だったため、改めて具体的に Walts は何であるか紹介します。

  • WaltsAngular(>=2.0.0)向けのライブラリです
  • RxJSを用いておりFluxアーキテクチャやReduxに影響を受けて開発したものです
  • 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ならばpayloadTという型である」と宣言せねばなりません。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リポジトリにはサンプル・アプリケーションの例がいくつか掲載されています。


Walts をどうぞよろしくお願いいたします。

Discussion