CANARY Cloud のディレクトリ戦略
カナリーでは、不動産の仲介会社様向けの顧客管理システムであるCANARY Cloudの開発と運用をしています!
今回は、CANARY Cloud で抱えていたディレクトリ構成の課題と改善方針について紹介したいと思います!
前提
CANARY Cloud では、Pages Router で Next.js を利用しています。
改善前のディレクトリ構成
src
├── apps
│ ├── crm # Canary Cloudの顧客管理画面に関するモジュール
│ │ ├── components # サービス内で共通利用されるコンポーネント
│ │ ├── hooks # サービス内で共通利用されるHooks
│ │ ├── lib # サービス内で共通利用されるライブラリ・汎用関数
│ │ ├── providers # サービス内のグローバルステート
│ │ ├── usecases # サービス内のAPI呼び出し
│ │ ├── utils # サービス内で共通利用される汎用的なutils
│ │ └── views # サービス内の画面コンポーネント
│ ├── admin # Canary Cloudのアカウント管理画面に関するモジュール
│ │ └── (crmと同じ構造)
│ ├── auth # Canary Cloudの認証画面に関するモジュール
│ │ └── (crmと同じ構造)
│ ├── management # Canary Cloudの顧客管理画面に関するモジュール
│ │ └── (crmと同じ構造)
│ └── portal # Canary Cloudのポータル管理画面に関するモジュール
│ └── (crmと同じ構造)
├── assets # 主にsvgを入れている
├── components # サービス間で共通して利用されるコンポーネント
├── config # 環境変数など設定周りに関するファイル
├── hooks # サービス間で共通して利用されるHooks
├── lib # サービス間で共通して利用されるライブラリ・汎用関数
├── pages # Next.jsの pages ディレクトリ
├── routes # ルーティングに関するロジック
├── styles # グローバルに適応させるcssを配置する
├── types # 全体で使うxx.d.tsを配置
├── usecases # サービス間で共通するAPI呼び出し
└── utils # 日付処理,api client等の汎用的なutil関数などを配置
課題
上記のディレクトリ構成で下記のような課題がフロント開発チーム内で挙げられました。
- src 直下のディレクトリが多い。
- apps が App Router へ移行したときに紛らわしくなる。
- ページ単位でコンポーネントを切っていることで、ページに依存したファイルとなってしまっており、別の箇所で同じロジック・コンポーネントを流用したいケースが発生した場合に、ページを跨がないといけなくなってしまう。
- usecase 層で API 接続とリクエスト生成・TanstackQuery によるキャッシュ管理等、責務が多くなってしまっている。
改善案1
まずは src 直下のディレクトリが多い問題について、移行できるものは移行することにしました。
utils
万能ディレクトリになっていたので、配下のファイルはそれぞれ関連のあるディレクトリ配下に移行する。もしくは新たにディレクトリを作成して分散させました。
hooks
hooks は lib 配下にすでに hooks ディレクトリが作成されていたため、そちらに移行しました。
styles
配下には index.scss ファイルしか存在していなかったので、pages 直下に移行しました。url からアクセスできないことは確認済です。
types
types ディレクトリは 2 ファイルしかなく、議論の末、config ディレクトリ配下に移行しました。
assets
デザインライブラリを lib 配下に作成しており、こちらが新しいデザインになっており、アイコン等で用いる svg 群は、デザインライブラリ直下の assets に格納されています。
そこで、古い svg 群が格納されている src 直下の assets に関しては、other というディレクトリ名でデザインライブラリ直下の assets に移行しました。
将来的には other 内で使われている svg は削除していきたいと考えています。
改善案 2
apps という命名が App Router へ移行したときに紛らわしくなるので、services というディレクトリに命名変更することとしました。
ここまでの対応で下記のように、シンプルで見やすいディレクトリ構成になりました。
src
├── services
├── components
├── config
├── lib
├── pages
├── routes
└── usecases
改善案 3
features アーキテクチャへの移行を決定しました。
これにより機能単位ごとで疎結合にでき、各 feature に編集を加えても、他の機能への影響が出ません。
features ディレクトリを新たに設けることによりステート変更による再レンダリングを最小限に抑えることができ、ページ毎に共通の機能やコンポーネントを用いる際にページを跨ぐ必要もなくなります。
また、機能毎にファイルがまとまっているので、再利用したい際などに探すのも容易になります。
features アーキテクチャへ移行するために、ページ単位のコンポーネントではなく features ディレクトリを作成して、ページ単位ではなく機能単位のコンポーネントや API 接続ファイル、TanstackQuery 依存ファイルを配置します。
src 直下のサービスを跨ぐ features と、services 配下の各サービスに閉じた features を新設して、features 配下にはバックエンドの domain 名のディレクトリを作成するというルールとしました。
具体的には下記のような features ディレクトリ(例)を src 配下と各 service 配下に新設する形となります。
features
├── domain1
│ ├── api-clients
│ │ └── domain1.ts # API接続関連をまとめる
│ ├── components
│ │ ├── Component1.stories.tsx # Storybook用
│ │ ├── Component1.test.tsx # テストファイル
│ │ └── Component1.tsx # 各featureに関連するコンポーネント
│ └── usecases
│ ├── queryKey.ts # キャッシュキーを管理する
│ ├── useGetDomain1.ts # TanstackQueryを用いてdomainの値を取得する
│ └── useUpdateDomain1.tsx # TanstackQueryを用いてdomainの値を更新する
│
└── domain2
usecases 配下のuseXXXX.ts
ファイルでは TanstackQuery の useQuery や useMutation を用います。その中のキャッシュキーの管理の部分ではqueryKey.ts
で作成したキーを指定します。
queryKey.ts
export const exampleKeys = {
all: () => [RESOURCE_NAME_MAP.EXAMPLE],
examples: () => [...exampleKeys.all(), "examples"],
examplesWithParams: (params: object) => [...exampleKeys.examples(), params],
example: (id: string) => [...exampleKeys.examples(), id],
} as const;
関数呼び出し部分では api-clients 配下に配置したファイルから必要な API 接続用関数を呼び出します。
作成したuseXXXX.ts
で export された関数を各コンポーネントから呼び出すことで、データの取得や更新を可能にします。
今後
まだ完全にディレクトリの改善が完了しているわけではないので、徐々にディレクトリ移行を行なっていきたいと思っています!残タスクは下記となっています。
- src 直下の usecase ディレクトリは廃止して features ディレクトリに移行したい。
- 各 services 配下の components ディレクトリはデザインライブラリまたは features ディレクトリに移行したい。
- views 配下で肥大したコンポーネント等も適切に機能ごとに分解して features ディレクトリに移行したい。
- src 直下の components ディレクトリは、不要なものは削除して必要なものはデザインライブラリとして移行したい。
- 改善案 1 で作成したデザインライブラリ assets 配下の other ディレクトリを削除する。※現在のデザインライブラリに従ったファイルの利用方法にする。
これらのタスクを完了した場合のディレクトリ構成になります。
src
├── apps
│ ├── crm
│ │ ├── features !New
│ │ ├── hooks
│ │ ├── lib
│ │ ├── providers
│ │ ├── utils
│ │ └── views
│ ├── admin
│ │ └── (crmと同じ構造)
│ ├── auth
│ │ └── (crmと同じ構造)
│ ├── management
│ │ └── (crmと同じ構造)
│ └── portal
│ └── (crmと同じ構造)
├── config
├── features !New
├── lib
├── pages
└── routes
終わりに
以上が、CANARY Cloud のディレクトリ戦略についてをまとめた記事でした!
「より詳細に話を聞きたい」「自分もこの開発に興味がある」と感じて頂けましたら、カジュアル面談などでお気軽にコンタクトをとっていただければと思います!
Discussion