はじめに
恥の多いFolder Structureで開発して来ました。
自分には、最適なFolder Structureというものが、見当つかないのです。自分はJavaの開発者として過ごした期間が長く、WebアプリケーションといえばSSRでしたので、Angularをはじめて見たのは、よほど大きくなってからでした。(引用元:「人間失格」 太宰治)
弊社では2020年12月頃からフロントエンドとしてAngualr(当時はv11)を採用したWebアプリケーションの開発を進めております。
ただ冒頭の駄文にあった通り、フロントエンド開発の知見の乏しさ故か、はたまた選定者(私)の愚かさ故か、オリジナリティを発揮し過ぎたフォルダ構成に端を発する開発者体験の低下が観測されるようになりました。
今回はその変遷と反省について書き綴っていこうと思います。
最初のフォルダ構成
最初の構成は以下の通りです。
app
|-- application
| |-- guard
| | |-- authorize-check.guard.spec.ts
| | |-- authorize-check.guard.ts
| | .. more
| |-- services
| |-- action-management.service.ts
| .. more
|-- domain
| |-- action
| | |-- action.factory.ts
| | |-- action.repository.ts
| | |-- action.ts
| |-- client
| |-- opportunity
| |-- organization
|-- infrastructure
| |-- api
| | |-- server-action-api.spec.ts
| | |-- server-action-api.ts
| | .. more
| |-- application
| | |-- action-management.service.spec.ts
| | |-- action-management.service.ts
| | .. more
| |-- domain
| |-- action
| | |-- action.factory.ts
| | |-- action.repository.ts
| | |-- action.ts
| |-- client
| |-- opportunity
| |-- organization
|-- presentation
|-- components
|-- directives
|-- pages
|-- pipes
|-- shared
app.module.ts
最初のフォルダ構成の問題点
domainパッケージ内に、バックエンド同様、永続化に関する制約をまとめていたのだけれど、フロントエンド側はinput要素に対してValidationをかけるので、フロントエンドにおける永続化層に来た時点(だいたいsubmitされた時)でValidationをかけるのはすこぶるUXが悪い。
また、Form側でValidationを担保するために最大文字数などの制約をdomainパッケージ内から呼び出すようにしていたのだが、Form実装時にちゃんとdomainパッケージ内から定数を呼び出しているかを担保出来ないためレビューの負荷が高くなってしまっていた。
FactoryやRepositoryについても、バックエンドと殆ど処理が被っている上に、バックエンドほど精緻な(トランザクションで守られた関係DBを用いた整合性の)チェックができるわけでもないのでやる意味あるのか?単に開発スピードを下げているだけでは?というのも思い始めていた。
次に選んだフォルダ構成
app
|-- core
| |-- components
| |-- guards
| |-- interceptors
| |-- services
|-- features
| |-- action
| | |-- components
| | | |-- forms
| | | |-- action-name
| | | | |-- action-name.component.spec.ts
| | | | |-- action-name.component.ts
| | | |-- action-type-select
| | | |-- action-datetime
| | |-- models
| | | |-- action.ts
| | |-- pages
| | | |-- add
| | | | |-- action-add.component.html
| | | | |-- action-add.component.scss
| | | | |-- action-add.component.spec.ts
| | | | |-- action-add.component.ts
| | | |-- detail
| | | |-- list
| | |-- services
| | |-- action-command.service.spec.ts
| | |-- action-command.service.ts
| | |-- action-query.service.spec.ts
| | |-- action-query.service.ts
| |-- client
| |-- opportunity
| |-- organizaiton
|-- shared
|-- validators
改善された点
主にコード量の削減の効果が大きかった
フォームの共通化
今までdomain内の値オブジェクト内にあったドメインの制約を各featureのforms集約することで「顧客名は最大200文字まで」といった情報を都度書くことがなくなった。
ドメイン層の削除
ドメイン層を経由した冗長な永続化機構を排除し、APIサーバーへ送る情報または送られてきた情報の詰め替えに特化したserviceへ切り替えたことで、コード量が削減された。
その他
compodocが見やすくなった笑
新たに生まれた問題
featureを跨るserviceやcomponentをどうするか
例えば、「顧客一覧取得service」があったとして、
顧客を管理する画面で顧客の一覧を表示する時に使うのか
それとも商談を作成する際の顧客選択フォームの内容を取って来る時に使うのか
...によって意味合いが変わるが、それを顧客のfeatureだけで管理し、商談は顧客のfeatureパッケージ内にあるするのか、顧客と商談それぞのfeatureで管理するのか
好みが分かれる問題だと感じる。
正解はあるのか
VUCAな時代のアプリケーション開発において、アプリケーションの進化の方向性というのを完全に予見することは不可能で、こういうFolder Structureであれば今後作成するあらゆるファイルを認知的不協和を起こさず配置できるというのはないと思います。
一緒に開発するエンジニアのバックグラウンドとか、フロントとバックでメンバー分けるのか、一緒にメンバーで作るのかなど、さまざまな変数を元に最適解を探していかなければいけないな、と感じます。
Framework側の進化によって解決される問題もありますしね。これからもAngularの成長に期待していきたいな、と思います。
以上、明日は@sosukesuzukiさんです。