Interactive

コロケーション

コードは種類ではなく、変更の単位で近くに置きます。一つの画面を修正するときに編集するファイルが、離れたディレクトリへ散らばらない構成を選びます。

選択基準

使用箇所が一つのコンポーネント、Hook、型、定数は、使う画面や機能のディレクトリへ置きます。プロジェクト直下のcomponents/hooks/へ、役割を問わず一括で分類しません。

共有する場所へ移すのは、二つ目の利用箇所が実際に現れてからです。将来の再利用を見込んだ先行配置はしません。tailwindのクラスが似ているだけの一致は共通化せず、業務上同じ意味を持つと確認できたものだけを昇格します。

app/invoices/
  page.tsx           # 画面の入口
  invoice-table.tsx  # この画面だけで使うコンポーネント
  use-invoices.ts    # この画面だけで使うHook

Next.jsのApp Routerでは、route以外のファイルはページとして公開されないため、routeディレクトリ内へ私有ファイルを置けます。TanStack Routerを使うプロジェクトでも、routeファイルの近くへ同じ基準で置きます。

共有する場所

プロジェクト直下のcomponents/は、shadcn/uiなど画面をまたぐ基盤UIに限定します。業務上の意味を持つコンポーネントは、複数の機能から使われるようになった時点で、機能間の共有ディレクトリへ移します。

昇格するとき、そのコンポーネントが元の画面の都合を引きずっていないかを確認します。特定画面のstateやデータ契約に依存したままの昇格は、共有ではなく癒着を広げます。

データ契約とテスト

データ契約は利用側の近くへ置きます。GraphQLのFragment、Hono RPCやRESTのendpointとqueryは、コンポーネントの「データとの境界」に従い、使うコンポーネントから追える場所に定義します。

テストは対象ファイルの隣へ.test.tsとして置きます。テストだけを別ツリーへ集約すると、実装の移動や削除でテストが取り残されます。

避ける構成

barrel file(index.tsでの再export)を各ディレクトリへ習慣で置きません。循環参照と不要なモジュール読み込みの原因になり、コロケーションで得た依存の追いやすさを打ち消します。importは実ファイルへ直接向けます。

種類別の深い分類(components/atoms/など)も採用しません。分類の粒度をめぐる判断が増えるだけで、変更の単位と一致しないためです。