L'architecture en couches
Le FSD organise le code en couches avec des règles de dépendance très strictes. De la plus haute (spécifique à l'application) à la plus basse (générique) :
src/
├── app/ /* Initialisation de l'application */
├── processes/ /* (Optionnel) Scénarios complexes inter-pages */
├── pages/ /* Les vues et le routage */
├── widgets/ /* Gros blocs UI autonomes */
├── features/ /* Interactions utilisateur spécifiques */
├── entities/ /* Entités métier */
└── shared/ /* Code infrastructurel réutilisable */Regardons en détail chaque couche avec des exemples concrets.
La couche app
C'est le point d'entrée. Elle contient la configuration globale (providers, routeur, styles globaux).
/* app/App.tsx */
import { StoreProvider } from './providers/StoreProvider';
import { RouterProvider } from './providers/RouterProvider';
import './styles/globals.css';
/* Seule cette couche a le droit d'importer absolument tout */
export const App = () => {
return (
<StoreProvider>
<RouterProvider />
</StoreProvider>
);
};La couche pages
Elle contient la définition des routes. Chaque page assemble des éléments des couches inférieures.
/* pages/product-details/ui/ProductDetailsPage.tsx */
import { ProductCard } from '@/entities/product';
import { AddToCartButton } from '@/features/add-to-cart';
import { ProductReviews } from '@/widgets/product-reviews';
/* On compose la page avec des widgets, features et entities */
export const ProductDetailsPage = () => {
return (
<div className="product-page">
<ProductCard product={product} />
<AddToCartButton productId={product.id} />
<ProductReviews productId={product.id} />
</div>
);
};La couche widgets
Ce sont de gros blocs UI indépendants qui mélangent plusieurs entités et features (ex: un header, une barre latérale).
/* widgets/header/ui/Header.tsx */
import { Logo } from '@/shared/ui/Logo';
import { SearchBar } from '@/features/search';
import { CartButton } from '@/features/cart';
/* Le widget a conscience du métier, mais pas de la page où il se trouve */
export const Header = () => {
return (
<header>
<Logo />
<SearchBar />
<CartButton />
</header>
);
};La couche features
Elle représente les actions de l'utilisateur (ajouter au panier, liker un post, se connecter).
/* features/add-to-cart/ui/AddToCartButton.tsx */
import { Button } from '@/shared/ui/Button';
import { useAddToCart } from '../model/useAddToCart';
/* Une action encapsulée avec sa propre logique métier */
export const AddToCartButton = ({ productId }) => {
const { addToCart, isLoading } = useAddToCart();
return (
<Button onClick={() => addToCart(productId)} disabled={isLoading}>
{isLoading ? 'Ajout...' : 'Ajouter au panier'}
</Button>
);
};La couche entities
Ce sont nos modèles de données métier (produit, utilisateur, commande). Elle contient l'affichage de base de la donnée, sans interactions complexes.
/* entities/product/ui/ProductCard.tsx */
import { Card } from '@/shared/ui/Card';
import { Price } from '@/shared/ui/Price';
/* Représente ce qu'EST la donnée, pas ce que l'utilisateur peut FAIRE avec */
export const ProductCard = ({ product }) => {
return (
<Card>
<img src={product.imageUrl} alt={product.title} />
<h3>{product.title}</h3>
<Price amount={product.price} />
</Card>
);
};La couche shared
C'est la fondation technique. Elle ne contient aucune logique métier (boutons UI génériques, utilitaires, configuration Axios).
/* shared/ui/Button/Button.tsx */
/* Un composant agnostique réutilisable partout */
export const Button = ({ variant = 'primary', children, ...props }) => {
return (
<button className={`btn-${variant}`} {...props}>
{children}
</button>
);
};