はじめに

今までフロントエンドのコンポーネント設計について、様々な考え方や手法を試してきましたが、どこまでいってもコードの重複と、共通化による影響範囲の拡大のトレードオフに悩まされてきました。

今回は、自分たちのチームで実際にうまくいった、現場寄りのフロントエンド設計について紹介します。

汎用コンポーネントを定義する

フロントエンドの設計で一番の悩みどころは、コンポーネントをどこまで共通化するかでした。
開発を進める中で、画面の仕様次第で似ているようで似ていないコンポーネントが増えていき、どこまで共通化するべきなのかが曖昧になっていきます。

そこで、自分たちのチームでは汎用コンポーネントを「BootstrapなどのUIライブラリの部品として存在していても違和感がないもの」として定義しました。

例えば、SelectDropdownModalは汎用コンポーネントとしますが、UserSelectはビジネス要件が含まれているため、複数の画面で使用されたとしても汎用コンポーネントとして定義しません。

機能を跨いだ共有を制限する

ディレクトリ構成は機能や画面単位で分け、機能を跨いで共有できるコンポーネントは先ほど定義した汎用コンポーネントのみに制限します。

機能によっては、汎用コンポーネントを少しカスタマイズして使いたい場面もあります。
もしそのカスタマイズが汎用コンポーネントの仕様として妥当であればオプションとして実装し、そうでないのなら重複コードが増えることを許容し、その機能のスコープ内でのみ使用するコンポーネントとして実装します。

このように制限することで、コンポーネントを変更した際の影響範囲が明確になり、変更容易性が高まります。
重複コードが増えることはコストですが、変更容易性を優先してレビューの負荷や予期せぬ不具合を減らせる方が、現場では価値が高いと考えています。

汎用コンポーネントをパッケージ化せずコピーする

こうして洗練させていった汎用コンポーネント群は、パッケージ化して社内で共有したくなるものです。
しかしカルテットでは、あえてパッケージ化せず、新規プロダクトを開発する際に汎用コンポーネントをディレクトリごとコピーする、という手法を取っています。

ここでも先ほどと同様の理由で、汎用コンポーネントのコードが重複するコストよりも、プロダクト毎の変更容易性を優先しています。

カルテットではプロダクト毎にチームが分かれているということもあり、汎用コンポーネントの変更による影響をチーム内に閉じられることは大きなメリットだと感じています。
その結果、チーム間のコミュニケーションコストが下がり、各チームの責任範囲も明確になります。

まとめ

今回は、実際の開発でうまくいったフロントエンドのコンポーネント設計について紹介しました。

コンウェイの法則が示すように、ソフトウェアの構造とチームの構造は無関係ではなく、今回紹介した設計も、組織やチームの体制によって向き不向きがあると考えています。

また、今後コーディングエージェントの活用が進み、コーディングのコストが下がっていくほど、コードの重複を減らすことよりも、変更の影響範囲が狭く明確であることの価値が相対的に高くなっていくのではないかと考えています。