【Angular】今の現場の問題を解決できそうなアーキテクチャを考えてみた


## 経緯
今の現場ではフロントエンドをTypeScript・Angularの組み合わせで開発しています。  

しかし私自身これまでコンポーネント指向開発をあまり経験してきませんでした。また現状フロントエンドの環境があまり整備されていない状況でした(詳細は後述)。

そのため、コンポーネント指向での開発に慣れる・今の現場での課題を解決できるようなアーキテクチャを考えてみる事を目的にアプリを作成した結果を書いていきます。  

[作成したアプリ](https://github.com/seiya2130/NBack)※最低限動くレベルのものです

## 方針
現状のフロントエンドで以下の課題を解決できるような、再利用性があり、責務が分かれているアーキテクチャにしていこうと考えました。

### 課題
- コンポーネントが再利用できない  
コンポーネントがページ単位で作成されていたため、フォームやボタンといった複数の画面で共通で使われるコンポーネントが再利用できませんでした。  

  それらのUIを修正するとなった場合に、各ページごとに修正対応が必要な状況でした。

- 責務が分かれていない  
上述した通りページ単位のコンポーネントには通信・表示・ビジネスロジックなどの責務が1つになっていたため、ファイルが巨大で数万行を超える場合もありました。  

  ファイルが巨大だと修正する際に該当箇所を見つけにくく、影響調査も時間が掛かります。

### 対応
- UIを再利用できるようにする  
複数の画面で使われるようなボタン・フォームといったUIは共通のコンポーネントとして作成する。
共通コンポーネントは渡された情報をただ表示する・クリックなどの操作を上位のコンポーネントに伝えるといったシンプルなものにする

- 責務を分割する  
1つのコンポーネントの責務を明確化し、1つのファイルが肥大化しないよう小さなコンポーネントにする。

## 構成
### コンポーネント
|  コンポーネント  |  責務  | 概要 |
| ---- | ---- | ---- |
|  model  |  データモデル  |  アプリケーション全体で利用するデータ　|
|  page  |  ページ遷移 | ページの遷移やクエリパラメータを渡す・受け取る  |
|  service  |  外部データの取得・更新  | storeとのやり取りやAPIを通じて外部との通信を行う※今回はstoreのみ利用 |
|  store  | 共通データの保持  | アプリケーション全体で利用するデータの保持　|
|  container  |  データの注入 | serviceやクエリパラメータから受け取った値をpresentationalへ渡す  |
|  presentational  |  ビジネスロジック・表示  | 受け取ったデータの加工・データの表示 |
|  ui  |  表示  | データの表示(presentationalよりも汎用的でアプリケーション全体で使えるUI) |

### 関係図
データを表示する際のコンポーネントの関係  (modelは複数箇所から呼ばれ図が複雑化するため省略)  
※規模が大きくなるとpresentational→uiの関係も出てきそうです
![アーキテクチャ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474060/43f68335-6760-55fd-d85f-db516f74695c.png)


### ディレクトリ構成
```
app
├─model
├─page
│  ├─container
│  └─presentational
├─service
├─store
└─ui
    ├─gui
    └─layout
```
Angularの[LIFT](https://angular.jp/guide/styleguide#lift)と呼ばれる考えを意識しました。

LIFTはコードを見つけやすく(Locate)、識別しやすく(Identify)、フラット(Flat)な構造でDRYを目指す(Try)アプリケーションを指します。
それぞれの観点で意識したことを書きます。

#### Locate
- pageごとにディレクトリを分け、対象のコンポーネントを見つけやすくする(機能追加や改修はまずはどのページか探すことが多いため)
- アプリケーション共通で使うmodel, service, store, uiはapp直下にする

#### Identify 
- コンポーネント名はxxx-page, xxx-service, xxx-containerとサフィックスを付けて何をするコンポーネント識別しやすくする

#### Flat
- app以下のディレクトリ階層は深くても3-4階層までで深くしすぎない

#### DRY
- uiディレクトリの下に共通コンポ―ネントであるgui(目に見えるもの、ボタンなど)とlayout(目に見えないもの、レイアウト関係)で共通化する

## 所感
フロントエンドのアーキテクチャについて初めて考えてみましたが、バックエンドのクリーンアーキテクチャのような代表例がなかったため設計が難しかったです。Atomic Designはよく見受けられましたが、粒度の判断が複雑になりそうなため今回は採用を見送りました。  

アーキテクチャ設計についてはもう少し抽象的な概念を理解しておく必要がよさそうだと感じました。
(抽象的な概念はバックエンドやフロントエンド問わず、ソフトウェア共通で適用できるものと考えているため)

### 良かった点
- 方針で挙げた課題は解決できた  
方針に記載したUIの再利用・責務の分割は達成することができました。

- コンポーネント指向開発に慣れた  
TypeScript・Angularを使ったコンポーネント指向の開発は自然と覚えることができたので、やっぱり作るものを決めてから開発した方がいいなと改めて感じました。(必要だと思ったタイミングにその技術を使って解決しようとする力は実務でも活きるため)

### 改善したい点
- presentationalとuiとの分別が難しい  
どちらもデータの表示を担当しており、他の画面で使われそうであればui、そのページでしか使われなさそうであればpresentationalにしています。ただ人によってその判断が曖昧になりそうだと感じました。
 
  そのため、presentationalは複数のuiを束ねて業務モデルを表すもの、uiは単体では意味を持たないものという、業務モデルを扱うかどうかにすればまだ判断しやすいと思いました。(これでもまだ曖昧かもしれませんが)

## 参考
- [WEB+DB PRESS Vol.112](https://gihyo.jp/magazine/wdpress/archive/2019/vol112)
- [Angular/React両方を経験して得られた堅牢なアプリケーション設計](https://dev.to/puku0x/angular-react-2h4j)
- [【Angular】Componentの設計方針](https://okkuso.com/component-design-in-angular/)
- [フロントエンドのコンポーネント設計に立ち向かう](https://qiita.com/seya/items/8814e905693f00cdade2)