はじめに
Reactでプロジェクトを始めるとき、最初に悩むのが「どんなディレクトリ構成にするか」という問題。個人的には、これが一番面倒な部分だと思っています。
create-react-appでプロジェクト作って、さてコンポーネントはどこに置く?hooksは?API呼び出しのロジックは?...そんなことを考えているうちに、気づいたらcomponentsフォルダに100ファイルとか、よくある話ですよね。
そんな設計迷子の救世主がBulletproof React。GitHubで33,800スター以上、5年以上の歴史を持つ、Reactアプリケーションのアーキテクチャガイドです。
正直なところ、「また設計論か」と最初は流してたんですよ。でも実際に読んでみたら、「これ、まさに自分が知りたかったやつだ」となりました。30代になって思うのは、設計で悩む時間って本当にもったいないということ。先人の知恵を借りるのが一番効率的です。
Bulletproof Reactとは
Bulletproof Reactは、本番環境で動くReactアプリケーションを構築するためのアーキテクチャガイドです。alan2207氏が作成し、MITライセンスで公開されています。
重要なポイントは、これがテンプレートやボイラープレートではないということ。「このまま使え」ではなく、「こういう考え方で設計するといいよ」という指針を示してくれるものです。
コードベースの92%がTypeScriptで書かれており、46人以上のコントリビューターが参加。実際のプロダクション環境での経験に基づいた知見が詰まっています。
特徴・メリット
1. フィーチャーベースの設計
これ、意外と重要な考え方なんですけど、「技術的な役割」ではなく「機能(フィーチャー)」でフォルダを分けるというアプローチ。
従来の構成だと、componentsフォルダにすべてのコンポーネント、hooksフォルダにすべてのhookを入れがち。でもこれ、プロジェクトが大きくなると「この機能に関連するファイルってどこにあるんだっけ?」と探し回ることになる。
フィーチャーベースなら、「認証に関するものは全部featuresの中のauthフォルダにある」と明確になります。コスパ的に、このアプローチは正義。
2. 明確なコードの流れ
「shared → features → app」という単一方向のコード流を推奨しています。
つまり:
- 共有コンポーネントは
sharedに置く - 各機能は
features内で完結させる - アプリケーション全体の設定は
appで行う
フィーチャー間のクロスインポートは避けるべきで、ESLintのimport/no-restricted-pathsルールで強制できます。これ、チーム開発では地味に効いてくる。
3. 9つの設計原則
Bulletproof Reactは以下の原則に基づいています:
- Easy to get started - 新メンバーがすぐに理解できる
- Simple - メンテナンスしやすいシンプルさ
- Right tools - 適切なツール選択
- Clear boundaries - 明確な境界線
- Team alignment - チーム内での認識統一
- Security - セキュリティへの配慮
- Performance - パフォーマンス最適化
- Scalability - スケーラビリティ
- Early issue detection - 問題の早期発見
個人的には、「Easy to get started」が一番大事だと思います。自分だけが分かる設計は、結局負債になる。
4. 包括的なドキュメント
プロジェクト構造だけでなく、以下のトピックもカバーしています:
- コンポーネント設計とスタイリング
- APIレイヤーの実装
- 状態管理パターン
- テスト戦略
- エラーハンドリング
- セキュリティベストプラクティス
- パフォーマンス最適化
- デプロイ手順
時短になる情報が一箇所にまとまっているのは、ありがたい。
プロジェクト構造
Bulletproof Reactが推奨するディレクトリ構成を見てみましょう。
基本構成
src/
├── app/ # アプリケーション層(ルート、メインコンポーネント)
├── assets/ # 画像やフォントなどの静的ファイル
├── components/ # 共有コンポーネント
├── config/ # グローバル設定
├── features/ # 機能ごとのモジュール
├── hooks/ # 共有カスタムフック
├── lib/ # 事前設定されたライブラリ
├── stores/ # グローバル状態管理
├── testing/ # テストユーティリティ
├── types/ # 共有型定義
└── utils/ # ユーティリティ関数
フィーチャーの内部構造
各フィーチャーは独立した構造を持ちます。
features/
└── awesome-feature/
├── api/ # APIリクエストとフック
├── assets/ # 静的ファイル
├── components/ # コンポーネント
├── hooks/ # フィーチャー固有のhooks
├── stores/ # フィーチャー固有の状態管理
├── types/ # 型定義
├── utils/ # ユーティリティ関数
└── index.ts # パブリックAPI
必要な部分だけを含めるのがポイント。全フォルダを作る必要はないです。
基本的な使い方
リポジトリの活用方法
Bulletproof Reactはライブラリではないので、インストールするものではありません。
# リポジトリをクローンして参照する
git clone https://github.com/alan2207/bulletproof-react.git
# または、docsフォルダの内容をGitHub上で読む
公式サイトは特にないので、GitHubのREADMEとdocsフォルダがメインの情報源です。
フィーチャーの作成例
認証機能を例に、フィーチャーの構成を見てみましょう。
// features/auth/api/login.ts
import { api } from '@/lib/api-client';
import { LoginCredentials, AuthUser } from '../types';
export const login = async (credentials: LoginCredentials): Promise<AuthUser> => {
const response = await api.post('/auth/login', credentials);
return response.data;
};
// features/auth/hooks/use-login.ts
import { useMutation } from '@tanstack/react-query';
import { login } from '../api/login';
import { useAuthStore } from '../stores/auth-store';
export const useLogin = () => {
const setUser = useAuthStore((state) => state.setUser);
return useMutation({
mutationFn: login,
onSuccess: (user) => {
setUser(user);
},
});
};
// features/auth/components/login-form.tsx
import { useLogin } from '../hooks/use-login';
export const LoginForm = () => {
const loginMutation = useLogin();
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
loginMutation.mutate({
email: formData.get('email') as string,
password: formData.get('password') as string,
});
};
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" placeholder="Email" />
<input name="password" type="password" placeholder="Password" />
<button type="submit" disabled={loginMutation.isPending}>
{loginMutation.isPending ? 'ログイン中...' : 'ログイン'}
</button>
</form>
);
};
// features/auth/index.ts - パブリックAPI
export * from './components/login-form';
export * from './hooks/use-login';
export * from './stores/auth-store';
フィーチャーの外からはindex.ts経由でのみアクセス。内部実装を隠蔽できます。
インポートの制限
ESLintで不正なインポートを防止できます。
// .eslintrc.js
module.exports = {
rules: {
'import/no-restricted-paths': [
'error',
{
zones: [
// featuresから別のfeaturesへの直接インポートを禁止
{
target: './src/features',
from: './src/features',
except: ['./index.ts'],
},
// appからshared以外への直接インポートを禁止
{
target: './src/app',
from: './src/features',
except: ['./index.ts'],
},
],
},
],
},
};
実践的なユースケース
中規模以上のプロジェクト
5人以上のチームで開発するプロジェクトでは、Bulletproof Reactの設計が特に効いてきます。
features/
├── auth/ # 認証機能
├── users/ # ユーザー管理
├── products/ # 商品管理
├── orders/ # 注文管理
├── notifications/ # 通知機能
└── settings/ # 設定画面
各フィーチャーが独立しているので、チームメンバーが別々のフィーチャーを並行開発できる。コンフリクトが減ってQOL上がります。
レガシーコードのリファクタリング
既存プロジェクトでも段階的に導入できます。
// 1. まず新機能からフィーチャーベースで作る
features/
└── new-dashboard/
├── api/
├── components/
└── index.ts
// 2. 古いコードは一旦そのまま
src/
├── components/ # 旧コンポーネント群
├── features/ # 新規フィーチャー
└── pages/ # 旧ページ群
// 3. 徐々に移行していく
一気にリファクタリングしようとすると大変なので、この段階的アプローチが現実的。
共有コンポーネントライブラリとの併用
// components/ui/button.tsx - 汎用的なボタン
export const Button = ({ children, variant, ...props }) => {
// 汎用的な実装
};
// features/checkout/components/checkout-button.tsx - 機能固有のボタン
import { Button } from '@/components/ui/button';
export const CheckoutButton = ({ onCheckout }) => {
return (
<Button variant="primary" onClick={onCheckout}>
購入手続きへ
</Button>
);
};
共有コンポーネントはcomponents/に、機能固有のコンポーネントはfeatures/内に。この使い分けが大事。
テスト戦略との統合
フィーチャー単位でテストも整理できます。
features/
└── auth/
├── __tests__/
│ ├── login-form.test.tsx
│ └── use-login.test.ts
├── api/
├── components/
└── hooks/
「このフィーチャーのテストはここにある」と明確になるのは、テストカバレッジを維持する上で重要。
まとめ
Bulletproof Reactを参考にして感じた変化:
- 設計で悩む時間: 激減。「どこに置くか」で迷わなくなった
- コードの見通し: フィーチャー単位で整理されてて把握しやすい
- チーム開発: 各自が別フィーチャーを担当できて効率アップ
- リファクタリング: フィーチャー単位で進められるので計画が立てやすい
- 新規参入者のキャッチアップ: 構造が明確なので理解が早い
正直なところ、「設計パターン」系の情報って抽象的すぎて使いにくいものが多いんですよ。でもBulletproof Reactは具体的な構成例とサンプルコードがあるので、実際に適用しやすい。
特に「フィーチャーベースの設計」という考え方は、今後のReact開発で標準的になっていくんじゃないかと思います。Next.js 13以降のApp Routerも、ある意味この思想に近いですしね。
まだ見たことない人は、一度GitHubのドキュメントを読んでみてください。自分のプロジェクトに合う部分だけピックアップして取り入れるのがおすすめです。全部採用する必要はない、というのもBulletproof Reactの良いところ。