とんかつ時々あんどーなつ

〜たとえ低空でも飛行していられるように〜

Angular アプリケーションのコンポーネント分割について考える

Angular のコンポーネント分割について考えていることや実践していることをメモ。

Presentation Domain Separation

前提として Angular アプリケーションは Presentation Domain Separation を推している。(と、僕は考えている)

コンポーネント内のロジックはビューに必要なロジックだけに制限します。他のすべてのロジックはサービスに委譲する必要があります。

コーディングスタイルガイド より

なので、コンポーネント(Presentation)とサービス(Domain)は適切に分離することがベストプラクティスとされている。 適切にとは何かやサービス側のさらなる分離についてはここでは言及しない。

コンポーネントの役割

ドメインロジックをサービス層へ委譲したとしてもコンポーネントにはまだ複数の役割がある。

  • スタイリング
  • ドメインロジックの呼び出し
  • URL パスとの紐付け
    • クエリパラメータやフラグメントへのアクセス

それぞれに名前をつけて責務を明確にすることで保守性が高まる。 具体的には単体テストなどで注力する部分が明確になりテストしやすくなるのではないか。

Presentational component

Container component

  • サービス層や store を通してデータのやり取りをするコンポーネント
  • 他の Container component や Presentational component を配置する
  • 基本的に表示に関することは何も気にしない
    • 場合によっては複数の子コンポーネントを配置するのでレイアウトに関するスタイリングを少し書いてもいいと個人的には思っている
    • レイアウトのスタイリングがあまりに多いようであれば Layout component を用意してもいいのではないか(要検討)

Page component

  • AppRoutingModule により URL パスと紐付けられるコンポーネント
    • router-outlet を通してのみ表示されるので selector の指定は不要
  • ActivatedRoute を通してクエリパラメータやフラグメントへのアクセスが可能
    • パスから取得したパラメータを保存するためにサービス層にアクセスすることがある
    • 逆にサービス層から何かを得ることはない
  • HTML は Containers component を置くだけでスタイルは管理しない
    • styles や styleUrls の指定も不要
  • xxxPageComponent という名前にするとエディタで探しやすい

例えば Tour of Heroes/detail/:id に対応する HeroDetailPageComponent を用意したとするとこのコンポーネントは spec を除くとこれくらいシンプルになる。(styles や styleUrls は不要で template もインラインで書けるくらいの量しかない)

import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  template: `<app-hero-detail></app-hero-detail>`,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HeroDetailPageComponent implements OnInit {
  constructor(private readonly _route: ActivatedRoute) {}

  ngOnInit(): void {
    // TODO
  }
}

まとめ

上記を踏まえて図にしてみた。

f:id:kasaharu:20210223123835p:plain

フロントエンドのコンポーネント分割で Presentational component と Container component はよく見かけるが、URL パスと紐付けを考える Page component を置いている。 Container component はよくロジックに専念すると言われることが多い印象だが、実際にロジックを持つのはサービス層になるためあくまでそことやり取りをするコンポーネントとしている。

Tour of Heroes を題材に試行錯誤している GitHub リポジトリを貼っておく - https://github.com/kasaharu/angular-architecture

参考