mcpField
F
FDD-architecture

FDD-architecture

github.com/jenishshrestha開発058 回インストール29 週間前に更新
リポジトリ

FDD-architecture について

React + Viteプロジェクト向けに、ビジネス機能単位でコードを整理するFDDアーキテクチャスキルです。コンポーネント、フック、型、テストを機能モジュールにまとめ、境界やインポート、命名、共有コードの指針を示します。

主な機能

  1. 01ビジネス機能単位のモジュール構成
  2. 02index.tsによる公開APIの定義
  3. 03機能内外のインポート規則
  4. 04ファイルとディレクトリの命名規則
  5. 05共有コードへの昇格指針

活用シーン

  1. 01React + Viteプロジェクトの構成設計
  2. 02機能モジュールの作成や再構成
  3. 03機能間インポートとAPI境界の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jenishshrestha/ai-skills --skill fdd-architecture

インストール後、Claude Code で/fdd-architectureを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

metadata.json
evals/
evals.json
references/
migration.md
rules/
named-imports.md
naming-consistency.md
locality-co-location.md
locality-depth.md
intra-feature-imports.md
hook-extraction.md
api-boundary.md
shared-component-organization.md
shared-global-move.md

詳細

React + Vite 向けの機能駆動アーキテクチャ(FDD)

技術的な種類ではなく、ビジネス機能ごとにコードを整理します。各機能は、独自のコンポーネント、フック、型、テストを持つ自己完結型のモジュールです。

プロジェクト構成

src/
├── features/           # Feature modules (business capabilities)
│   ├── auth/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── lib/
│   │   ├── types/
│   │   ├── index.ts   # Public API
│   │   └── auth.test.tsx
│   └── dashboard/
│       └── ...
├── shared/            # Shared infrastructure (3+ feature rule)
│   ├── components/
│   │   ├── ui/        # Primitive components (Button, Input)
│   │   ├── layouts/   # Layout wrappers (Header, Sidebar)
│   │   └── providers/ # Global providers (ThemeProvider)
│   ├── hooks/
│   ├── lib/
│   └── types/
└── app/               # App entry and routing
    ├── routes/
    └── main.tsx

React の品質原則

これらはアーキテクチャだけでなく、このプロジェクトで記述するすべてのコードに適用されます。

KISS — シンプルに保つ

気の利いた抽象化よりも、標準的な React のパターンを優先します。コメントなしではロジックを理解しにくい場合は、シンプルにしてください。useState で十分なときに、複雑な状態管理に頼らないでください。

YAGNI — 今必要なものだけを作る

「念のため」の props、推測に基づく抽象化、時期尚早な最適化を追加しないでください。今必要なものを作りましょう。現時点で利用する箇所がない prop は削除してください。

設定よりコンポジション

コンポーネントに 10 個以上のブール値の props が蓄積してきたら、複合コンポーネントパターンを使って、組み合わせ可能なパーツに分割するサインです。

// ❌ Configuration overload
<Card showHeader showFooter isCollapsible hasBorder variant="outlined" />

// ✅ Composition
<Card>
  <Card.Header>Title</Card.Header>
  <Card.Content>Body</Card.Content>
  <Card.Footer>Actions</Card.Footer>
</Card>
明示的な予測可能性

useEffect に隠れた副作用を持たせないでください。明示的なイベントハンドラーと安定したコールバック参照を優先します。何かが起きたとき、読み手が effect の依存配列を探し回らなくても、その理由をたどれるようにしてください。

// ❌ Hidden side-effect — runs on every query change
useEffect(() => {
  trackAnalytics("search", { query });
}, [query]);

// ✅ Explicit — fires on user action
const handleSearch = (query: string) => {
  setQuery(query);
  trackAnalytics("search", { query });
};
アクセシビリティ(a11y)

すべてのインタラクティブ要素で、キーボード操作への対応、適切な ARIA 属性、視認できるフォーカスインジケーターが必要です。role 属性を持つ汎用的な div よりも、セマンティックな HTML 要素(button、nav、main)を使用してください。

FDD のルール — クイックリファレンス

1. 機能の局所性(最重要)

機能のコードはすべて src/features/[feature-name]/ の中に置きます。ネストは最大 3 階層に制限してください。

src/features/user-profile/
├── components/
│   ├── profile-card.tsx
│   └── avatar-upload.tsx
├── hooks/
│   └── use-profile.ts
├── lib/
│   └── format-name.ts
├── types/
│   └── profile.ts
├── index.ts
└── user-profile.test.tsx
2. 公開 API の境界(重要)

各機能は index.ts を通じてエクスポートします。機能の内部実装から直接インポートしないでください。

// src/features/auth/index.ts
export { LoginForm } from "./components/login-form";
export { useAuth } from "./hooks/use-auth";
export type { AuthUser, AuthState } from "./types";

// ❌ Importing from internals
import { LoginButton } from "@/features/auth/components/login-button";

// ✅ Importing from public API
import { LoginButton } from "@/features/auth";
3. インポート規約(重要)

機能内 — 相対パスを使用します。

import { useAuth } from "../hooks/use-auth";

機能間 — 公開 API を介したエイリアスを使用します。

import { useAuth } from "@/features/auth";

型 — 常に import type を使用します。

import type { User } from "./types";
4. 命名規約(中程度)
種類規約例
コンポーネントファイル(.tsx)PascalCaseProductCard.tsx、DataTable.tsx
フックファイル(.ts)use を接頭辞にした camelCaseuseDataTable.ts、useUserProfile.ts
型ファイルkebab-case + .types.tsproduct.types.ts、user-profile.types.ts
スキーマファイルkebab-case + .schema.tsauth.schema.ts、product.schema.ts
フォルダーkebab-caseuser-profile/、data-table/
ユーティリティ / lib / api / configkebab-casefetch-users.ts、utils.ts

名前付きインポートのみを使用し、import * は使用しないでください。詳細と例外については、rules/naming-consistency.md を参照してください。

5. 共有インフラストラクチャ(中程度)

三回ルール — src/shared/ に移すのは、3 つ以上の機能で使われるようになってからにします。

  • 1 つの機能 → 機能フォルダー内に置く
  • 2 つの機能 → 元の場所に置くか、ロジックが異なる方向に進化しそうな場合は複製する
  • 3 つ以上の機能 → src/shared/ に移す
6. フックの切り出し

次の場合は、ビジネスロジックをコンポーネントから切り出します。

  • コンポーネントに、レンダリング以外のロジックが 5 行以上ある
  • ロジックを機能内で再利用できる
  • ロジックを単独でテストする必要がある
// ❌ Logic mixed with UI
function ProfileCard() {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    fetch('/api/user').then(res => res.json()).then(setUser);
  }, []);
  return <div>{user?.name}</div>;
}

// ✅ Logic extracted to hook
function ProfileCard() {
  const { user, loading } = useUserProfile();
  return <div>{user?.name}</div>;
}

詳細ルール

詳しい説明、例、エッジケースについては、rules/ 内の該当するルールファイルをお読みください。必要なものだけを読み込んでください。

ルールファイル読み込むタイミング
locality-co-location.md新しい機能を作成するとき、ファイルの配置場所を決めるとき
locality-depth.md機能フォルダーの階層が深くなり、再構成が必要なとき
api-boundary.mdindex.ts のエクスポートを設定するとき、機能間のインポートを確認するとき
intra-feature-imports.md相対インポートと絶対インポートのどちらを使うか決めるとき
naming-consistency.md新しいファイルやフォルダーに名前を付けるとき
named-imports.mdインポート形式に関する質問、バレルファイルの設定
shared-global-move.mdコードを共有領域に移すべきか決めるとき
shared-component-organization.mdsrc/shared/components/ のサブディレクトリーを整理するとき
hook-extraction.md複雑なコンポーネントからロジックを抽出するとき

ツール

Vite のパスエイリアス(vite.config.ts)
export default defineConfig({
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
      "@/features": path.resolve(__dirname, "./src/features"),
      "@/shared": path.resolve(__dirname, "./src/shared"),
    },
  },
});
TypeScript のパス(tsconfig.json)
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@/features/*": ["./src/features/*"],
      "@/shared/*": ["./src/shared/*"]
    }
  }
}
ESLint — 公開 API の境界を強制
{
  "rules": {
    "no-restricted-imports": [
      "error",
      {
        "patterns": [
          {
            "group": ["@/features/*/components/*", "@/features/*/hooks/*"],
            "message": "Import from feature's public API (index.ts) only"
          }
        ]
      }
    ]
  }
}
機能ベースのコード分割(TanStack Router)
const dashboardRoute = createRoute({
  path: "/dashboard",
  component: () => import("@/features/dashboard").then((m) => m.DashboardPage),
});

移行ガイド(フラットな構成 → FDD、Next.js → Vite)については、references/migration.md をご覧ください。

コメント