React.js: コンポーネントをどう分けるか

React.js をチームで開発していると、どう言った粒度でコンポーネント分けるか、という議論になる。DRY の原則 (Don't Repeat Yourself) に従い、再利用を加速させ、結果としてチーム全体の生産性を上げるために、避けては通れない議論だ。

一つの解決策として、Atmoic Design というコンセプトがある。これは、「どうコンポーネントを分けるか」という判断をする際に便利となる判断尺度である。要するに、「ものさし」のようなものだ。

僕はこの Atomic Design を本番の業務アプリケーションの開発で導入したことがある。結果としては、微妙だった。導入初期は、複雑怪奇だったフロントエンドを、ある程度の保守可能なレベルまでブレイクダウンすることができた。

しかし、チームメンバーが入れ替わり立ち替わり変わっていく中で問題となったのは、「このコンポーネントは Atom なのか Molecules なのか」または「このコンポーネントは Molecules なのか Organisms なのか」を決める際に、個人間の主観が入ってなかなか決まらない ということだ。

Atoms は分かる。最小限のビルディングブロックだ。Organisms とも明らかに違う。しかし、Molecules の扱いが難しい。「そこそこ大きいが、そこまで大きくない」中途半端なコンポーネントが、Molecules に乱立することになる。props の違いやちょっとしたレイアウトの違いがある Molecules が乱立した結果、再利用しづらい Organisms が出来上がる。

Atomic Design の限界は、「レイアウト・見た目をどう分けるか」については話しているが、「機能をどう分けるか」については規定していない 点にある。レイアウトと機能は厳密には違う。例えば、「スクロールしたら非表示にする」という「機能」は、ページ上部に位置するヘッダーにも下部に位置するフッターにも適用できる。この時、ページ上のスクロールイベントや座標軸を取得し計算し状態を変化させるビジネスロジックは、どこに来るか。

React.js の提唱しているデザインパターンの一つが、Higher-Order Components (HoC) パターンだ。Hooks を利用している場合は、自前の Hooks を書いても良い。

ここで、機能とレイアウトという直行する概念を、どう分けていくか という話になる。チーム開発である限り、可能な限り主観によって判断が変わるような状況は避けたい。Ruby on Rails が敷いている Convention Over Configuration のような、どのファイルをどこに配置するのかが半自動で決まるような規律というものを、コンポーネントの分離に導入するためには、何かが足りない。

対処療法的な作戦としては、Material UI や React Bootstrap が提供するような、独自デザインフレームワークを社内で開発するという手法がある。もしくは、それらフレームワークをそのまま使っても良い。レイアウトの分け方というのを、そのデザインフレームワークが提供するレベルにとどめ、独自開発するコンポーネントはすべてフラットに管理する。

この作戦は、スケールしない。小さいアプリケーションなら良いが、アプリケーションが進化し複雑化するにすれ、どこかで必ずコンポーネントをどう分けていくかという問題と立ち向かっていく必要がある。

ある程度複雑化してきたコンポーネント群は、private NPM packages として切り出してしまうという作戦もある。これは有効だと思うが、これだけでは小さいコンポーネント群をどう切り分けるかという議論に対して答えを出していない。また、抽象化できていない段階で先走ってパッケージとして切り出してしまうと、無駄に保守性が悪化してしまうという話になる。

そもそも、ソフトウェアというのは進化するものである。とあるコンポーネントを最初に Atoms だとか Molecules だとか規定したからと言って、ソフトウェアの進化に伴って適切に Re-organize させていくことさえできれば良いのかも知れない。変化に対する柔軟性と弾性をどう確保するか、という話に置き換えられる。

今の所、結論は出ていない。ぜひここら辺について詳しい方や思いのある方々と議論し尽くしてみたいものである。

2022-11-13