19
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Angular】Angularを1年間触ってみてたどり着いたコンポーネント設計: Page-Layout-Presenter構造

Last updated at Posted at 2021-03-13

Page-Layout-Presenter構造

フロントエンド開発において最適なコンポーネント設計について考えることは避けては通れないと考えています。
未熟ながら私もいくつか考えてみました。今回はその中で、ある程度形になったものを一つ提案したいと思います。

今回提案する設計では、チームでの開発を想定し、長期的に破綻せず運用でき、修正に強く、直感的であることを目指しました。
少しでもAngularの開発の手助けになればと思っています。

考えた経緯については別記事にまとめてあります。ご興味のある方は併せてご覧ください。
【フロントエンド】最適なコンポーネント設計について考えてみた

設計の全体像

参照構造

詳細を説明する前に、大まかにでも全体を見ていただきたいので、実装した際のコンポーネントなどの参照構造をまずは図で紹介します。
赤色は@NgModule、黄色は@Component、水色は@Injectableを持つクラスを指します。
少し複雑に見えるかもしれませんが、詳細は後ほど説明します。

Angular Page-Layout-Presenter architecture image

ディレクトリ構造

次にディレクトリ構造を紹介します。
これについても詳細は後ほど説明します。

なお、拡張子が.spec.tsのファイルは表示を省略しています。
また、繰り返しなどは...で省略しています。
そして、次のようなコンポーネント内のファイルはsome/some.component.*として省略して表記しています。

コンポーネント内のファイル
some/
 ├ some.component.html
 ├ some.component.scss
 ├ some.component.spec.ts
 └ some.component.ts

=> some/some.component*

全体のディレクトリ構造は次です。

ディレクトリ構造の全体像
src/
 └ app/
  ├ app.module.ts
  ├ app-routing.module.ts
  ├ app.component.*
  ├ apis/
  │ └ ...
  ├ services/
  │ └ ...
  ├ directives/
  │ ├ directives.module.ts
  │ ├ no-tag.directive.ts
  │ └ ...
  ├ components/
  │ ├ material-design/
  │ │ └ material-design.module.ts
  │ └ presenters/
  │  ├ presenters.module.ts
  │  ├ buttons/
  │  │ ├ buttons.module.ts
  │  │ ├ button/button.component.*
  │  │ ├ yellow-button/yellow-button.component.*
  │  │ ├ white-button/white-button.component.*
  │  │ └ ...
  │  ├ texts/
  │  │ ├ texts.module.ts
  │  │ └ ...
  │  ├ images/
  │  │ ├ images.module.ts
  │  │ └ ...
  │  └ ...
  └ features/
   ├ feature1/
   │ ├ feature1.module.ts
   │ ├ feature1-routing.module.ts
   │ ├ services/
   │ │ └ feature1.service.ts
   │ └ components
   │  ├ pages
   │  │ ├ some1-page/some1-page.component.*
   │  │ ├ some2-page/some2-page.component.*
   │  │ └ ...
   │  └ layouts
   │   ├ some1-layout/some1-layout.component.*
   │   ├ some2-layout/some2-layout.component.*
   │   ├ some3-layout/some3-layout.component.*
   │   └ ...
   └ feature2/
    └ ...

設計の詳細

今回提案する設計では、コンポーネントを適切に分割することに加え、チームでの開発を想定し、長期的に破綻せず運用でき修正に強く直感的であることを目指しました。

そのために、コンポーネントの分割方法や、サービスやディレクティブ、スタイルシート、モジュールの作成などについて、総合的に考えました。ここではそれらについて大まかに説明します。

ルーティングの参照構造

Angularにもルーティングの機能があり、クライアントから受け取ったURLを基に、表示するコンポーネントなどを指定することができます。

features

ここではアプリケーションがもつ機能をfeatureと呼ぶことにします。
例えばlogintodoなど、それらはアプリケーションによって異なります。
ルーティングを有効にする場合は、https://localhost:4200/loginloginように、アプリケーションのURLの直後の部分がfeatureにあたります。

これはつまり、各feature<feature>.module.ts<feature>-routing.module.tsを持ち、さらに、ルーティングの一番親であるapp-routing.module.tsから参照されるということです。

このルーティングの参照関係を図にすると次のようになります。

routing module refference graph

feature同士は互いに関与しないため、並行して開発しても衝突しません。また、あるfeatureがバグを含んでいても、他のfeatureは正常に動かせます。なので、エンジニアがfeature毎に分担するという開発方法が考えられます。

コンポーネントの親子構造

コンポーネントには親子構造があり、次のような層構造を持たせます。

(親) => (子)
App Component => Page Components => Layout Components => Presenter Components

ここではそれぞれの層について説明します。

App Component

  • アプリケーション新規作成時に自動で作成されます。
  • アプリケーションに対し一つのみ存在し、親子関係の頂点になります。

Page Components

  • これはウィンドウ一面に表示されるページを表すコンポーネントで、主にLayout Componentsで構成されます。
  • feature毎にページを作成するため、src/app/features/componentsディレクトリに配置します。
  • URLに対し一つだけとし、使いまわしはしません。
  • 状態管理に関するサービスはPage Componentsでのみ注入します。
    • アプリケーションの表示に関わる具体的なデータのことを「状態」と呼びます。
    • これは、ほかの方の設計などで見られるContainer Componentの役割と言えますが、Container Componentはページに対して一つとされる場合が多いので、今回は冗長性を減らすためにPage Componentsに取り込んでいます。
    • 状態管理にはNgRxの利用が考えられますが、コストが高いため最初はNgRxを利用せず、サービスのみで開発するという方法も考えられます。NgRxInterceptorについては勉強中のため、今回は説明を省略させていただきます。
    • これについて図で表すと次のようになります。
      angular-architecture-overview2-Page-10.png
  • 子コンポーネントから@Output()でイベントを受け取り、必要であれば状態を子コンポーネントの@Input()へ返します。
  • background-colorなど、ページ全体に対するスタイルはこのコンポーネントに記述します。

Layout Components

  • Page Componentsと同じく、src/app/features/componentsディレクトリに配置します。
  • Presenter ComponentsLayout Componentsで構成されます。
  • Layout Componentsは他のLayout Componentsを参照可能なので、これにより複雑なレイアウトも表現可能です。
  • Layout Componentsは状態を持ちません。
    • ただし、コンポーネント内で完結するような状態は除きます。
  • 親コンポーネントから@Input()で受け取った状態を利用し、必要であれば子コンポーネントの@Input()へ返します。
  • また、子コンポーネントから@Output()でイベントを受け取り、親コンポーネントへ@Output()します。
  • displaymarginなど、レイアウトに関するスタイルはこのコンポーネントが担います。逆に、見た目に関するスタイルはここには記述しないようにします。

featuresPage ComponentsLayout Componentsを図にすると次のようになります。

angular-architecture-overview2-Page-9.png

Presenter Component

  • Presenter Componentsはページにおける最小パーツです。
  • Presenter Componentsはアプリケーション全体で参照可能なコンポーネントです。
    • そのため、src/app/features/componentsディレクトリではなくsrc/app/componentsディレクトリに配置します。
  • Presenter Componentsは一つのPresentersModuleを持ちます。
  • Presenter Componentsは複数のPresenterで構成されていて、PresentersModuleは各Presenterが持つPresenterModuleをimportします。
  • Presenter内でAngular Material Designを利用する場合はそのPresenterModuleMaterialDesignModuleをimportします。
  • Presenterは一つのGUI Parts Componponentとそれを継承する複数のStyled GUI Parts Componentsを含んでいます。
GUI Parts Component と Styled GUI Parts Components
  • Styled GUI Parts Componentsとは、GUI Parts Componentを継承し、その上で見た目に関するスタイルを記述したコンポーネントです。
    • 例えばbuttonsというPresenterの場合、buttonGUI Parts Component、それ以外がStyled GUI Parts Componentsとなります。この例のディレクトリ構造と図は次のようになります。
src/app/components/presenters/
 ├ presenters.module.ts
 └ buttons/
  ├ buttons.module.ts
  ├ button/button.component.*                // GUI Parts Component
  ├ yellow-button/yellow-button.component.*  // Styled GUI Parts Component
  ├ white-button/white-button.component.*    // Styled GUI Parts Component
  └ ...                                      // Styled GUI Parts Component

angular-architecture-overview2-Page-5.png

  • Presenter Componentsは状態を持ちません。
    • ただし、コンポーネント内で完結するような状態は除きます。
  • GUI Parts Componentにて、親コンポーネントから状態を受け取る@Input()と親コンポーネントへイベントを返す@Output()を記述し、Styled GUI Parts Componentsではそれを継承します。
  • Presenter Componentsは一度作ればほかのアプリケーションにも移植でき、その意味でも再利用性が高いです。

これらのことを踏まえると、コンポーネントの親子関係の図は次のようになります。

angular-architecture-overview2-Page-6.png

また、モジュールやサービスも含め、以上のことをまとめると、記事冒頭にある図と同じになります。

Angular Page-Layout-Presenter architecture image

おわりに

今後は構築手順についても追記していきたいと考えています。
また、引き続きよりよい設計についても私なりに考えていきたいと思います。
一部でも参考になれば幸いです。

19
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?