My App
Architecture/FSD

FSD avec Next.js

Si vous utilisez Next.js avec le App Router, vous allez faire face à un conflit : Next.js possède déjà un dossier app/ réservé au routage, ce qui rentre en collision avec les couches app et pages du Feature Sliced Design.

Comment faire cohabiter les deux ? Il existe deux grandes approches dans l'industrie.

Méthode 1 : L'hybride

Dans cette approche, on accepte que Next.js dicte la loi sur le routage. Le dossier app/ de Next.js absorbe et remplace les couches app et pages du FSD.

  • La configuration globale (providers, styles) du FSD va dans le layout.tsx de Next.js.
  • Les vues (la couche pages du FSD) vont dans les page.tsx de Next.js.
  • Le reste de l'architecture (widgets, features, entities, shared) reste intact à la racine du dossier src/.
src/
├── app/                  /* Le App Router de Next.js (Fusion de app et pages FSD) */
│   ├── layout.tsx        /* Remplace la couche app (Providers, CSS globaux) */
│   └── product/
│       └── [id]/
│           └── page.tsx  /* Remplace la couche pages (Assemble widgets et features) */
├── widgets/              /* FSD classique... */
├── features/
├── entities/
└── shared/

Méthode 2 : L'approche Agnostique

Si vous voulez garder une architecture FSD 100% pure et que vous considérez Next.js comme un "simple détail d'implémentation" (ce qui permettrait de migrer vers un autre framework comme Remix plus tard), on sépare les deux.

On crée une architecture FSD complète dans le dossier src/, et le dossier app/ de Next.js (à la racine) ne sert que de "pont" ou de "routeur bête".

app/                      /* Le routeur Next.js (à la racine) */
├── layout.tsx            /* Importe le composant App de src/app/ */
└── product/
    └── [id]/
        └── page.tsx      /* Fait juste un import : return <ProductDetailsPage /> */
src/                      /* L'architecture FSD pure */
├── app/                  /* Les providers, le store */
├── pages/                /* ProductDetailsPage.tsx */
├── widgets/
├── features/
├── entities/
└── shared/

Dans le fichier app/product/[id]/page.tsx de Next.js, on ne trouvera aucune logique, juste un appel vers la couche FSD :

/* app/product/[id]/page.tsx (Routeur Next.js) */
import { ProductDetailsPage } from '@/pages/product-details';

export default function NextProductPage({ params }) {
  /* Le routeur passe juste les paramètres à la page FSD */
  return <ProductDetailsPage id={params.id} />;
}

Laquelle choisir ? La Méthode 1 est recommandée pour 90% des projets car elle évite de dupliquer l'arborescence des routes et tire pleinement parti de la simplicité du App Router de Next.js.