Marginalia

システムの学習曲線とAngularアプリケーションの設計

だいぶ決めつけと仮定で書いているので鵜呑みにしないで議論のタネということで。


システムの開発が持続可能であるための条件はざっくりまとめれば次の 2 つに尽きる。

  1. 開発者を持続的に手に入れられること
  2. 実行環境を持続的に手に入れられること

つまり、誰でも開発に参加できて、どこでも実行できるなら、そのシステムの開発持続性は高い。言い換えれば、開発持続性が高いということは、高いスキルへの依存と環境への依存が小さいということになる。可読性や可用性、メンテナンス性といった要素はこの 2 つの持続性に吸収される。

高いスキルに依存しない開発

開発に参加しやすいということは、システムを理解するのに必要なコストが少ないということである。言い換えれば、要求する前提知識が少なく、わかりやすいということだ。 システムを理解するためにかかる時間の内訳は次のようなものがある。

  • システムの前提にあるドメイン知識を理解する時間
  • システムの構成技術を理解する時間
  • システムの構造を理解する時間
  • システムの開発ルールを理解する時間
  • システムの要件を理解する時間
  • システムの振る舞いを理解する時間

これらは複雑に絡み合っていて、すべてを減らすことは難しい。

そこで取るべき戦略は、段階的に理解を深めていきながら参加できるように学習曲線を設計することだ。システムをモジュール化し、モジュールごとに要求する前提知識を明確にすることで、参加しやすい環境を作ることができる。 Web サービスに例えるなら直帰率を下げ回遊させながらコンバージョンを狙うような戦略だ。

Angular アプリケーションの場合

およそ 1 年前に書いたこの記事は、我ながらいいところをついていた。

持続可能な Angular アプリケーション開発のために大事なこと - lacolaco

フレームワークに依存する領域と非依存の領域を明確に分けるということは、フレームワークの前提知識が少なくても参加できる領域を作ることになる。

Angular アプリケーションのすべての領域を理解するためには Web、TypeScript、Angular、RxJS の基本知識、さらにアプリケーションによっては NgRx や Angular Material、その他サードパーティライブラリの知識が必要になっていくだろう。そんなものを最初から求めていては開発チームはスケールできない。

より基本的な知識、誰もが知っているだろう知識、あるいは採用戦略にあわせて、適切にアプリケーションをモジュール化していかなくてはならない。

ここからはそうした目的における設計のパターンを提案する。これは責務に応じたモジュール化のための指針ではなく、モジュール群をカテゴライズするための指針である。

コアユニット: TypeScript とドメインの理解

Angular アプリの開発チームに新たなメンバーを迎えるにあたって、最低限要求すべき前提知識は次の 2 つである。

  • ドメインへの理解
  • TypeScript 言語への理解

アプリケーションが対象とする事業ドメインについては、社員であれば当然理解しているべきだろう。そして新たなプログラミング言語を学ぶことは、何か別の言語での開発経験があればそれほど難しいものではない。

自社のドメイン知識があり、TypeScript さえ理解できていれば参加できる領域を作ることで、その領域のスケーラビリティは大きく改善する。この領域を「コアユニット」と呼ぶことにする。

「ドメイン層」と呼ばれる類のモジュールは、このコアユニットに該当する。ライブラリやフレームワーク、あらゆる外部環境に依存せず、純粋な TypeScript の型定義やクラス、関数、定数などで構成される。 プレゼンテーション/ドメイン分離(PDS)が済ませたコンポーネントとサービスから構成される状態の Angular アプリケーションから、まずはコアユニットを切り出す事が重要だ。

ビジュアルユニット: HTML/CSS の理解

次に多くの開発者が理解できる領域は、HTML/CSS が理解できれば扱えるモジュールが属する。ここでは「ビジュアルユニット」と呼ぶこととする。 ただし、Angular のコンポーネントのすべてがビジュアルユニットに含められるわけではない。なぜならこのユニットでは RxJS や Angular のコンポーネント機能への十分な理解は求められないからだ。「Presentational Component」や「Dumb Component」と呼ばれるような、アプリケーションの末端に位置するコンポーネントがここに該当する。 {{ data }} という補完構文レベルの知識は要求されるが、複雑な Directive や Pipe の活用はこのユニットには含められない。

インフラユニット: 外部 API の理解

次に理解しやすい領域は、ブラウザが持つ Web API や、Google Analytics のような完全にシステムの外部にある API とアプリケーションをつなぐ部分だ。ここでは「インフラユニット」と呼ぶ。「アプリケーションサービス」や「アダプター」と呼ばれるようなモジュールはこのユニットに多く該当する。Visual とは反対の位置でアプリケーションの末端に位置するこのユニットは依存対象が少なく、モジュールの責務が単一で明確に維持しやすい。新しく参加したメンバーでもそのモジュールが果たすべき役割をすぐに把握できるだろう。

アプリケーションユニット: RxJS/Observable の理解

「アプリケーションユニット」と呼ぶ領域には、RxJS への理解が不可欠だ。Angular アプリケーションの構築において、ほとんどのデータは Observable としてやりとりされる。JavaScript における Promise と同じレベルの存在として Observable という概念に慣れていなければ、アプリケーションの振る舞いに触れることはできない。ドメイン知識と TypeScript、RxJS の知識、そしてアプリケーション全体の大まかな構造が見えてくると、このユニットの開発へ参加できる。「状態管理」や「ユースケース」と呼ばれるようなモジュールがこのユニットに該当する。

また、もし NgRx のような状態管理ライブラリを利用すると、その知識も要求される。ただし、このユニットに Angular API への関心を持たせないことが重要である。ちなみに @Injectable() については Angular API と捉えず妥協する。フレームワークが提供する DI システムとして利用する。

Angular ユニット: Angular API の理解

最後に残った部分が、Angular への理解を高く求められる領域である。Store とテンプレートをつなぐ「Container Component」や、フォーム管理、ルーティングなど、コンポーネントシステムに限らず Angular API への関心が強い部分だ。当然 RxJS も理解しておく必要がある。

最終的なユニット分割の依存関係を見ると、依存される末端のユニットほど参入障壁が低いように設計されていることがわかる。そしてアプリケーションの中心に近づくにつれて階段を登るように要求スキルがあがっていくようになっている。

参入しやすさを設計する

Angular アプリケーションの開発持続性を高め、スケーラビリティを確保するにはいかに Angular ユニットを小さく作るかにかかっている。UI の見た目に関する責務は HTML と CSS だけわかっていれば書けるビジュアルユニットへ逃し、ロジックは TypeScript と RxJS がわかっていれば書ける Application ユニットへ逃がす。そしてさらに Pure TypeScript で書ける部分はコアユニットへ逃していく。

参入障壁が低いユニットに取り組み始めたメンバーは、アプリケーションへの理解度を高めながら、緩やかな学習曲線を描いていく。慣れてきたら別のユニットに取り組む。これを繰り返すことで結果的には多くのメンバーが Angular ユニットまで早く到達できるだろう。