
Lorsque vous faites ng new , le CLI d'Angular génère un certain nombre de fichiers et de dossiers, et toute l'application tient dans un seul dossier. À partir de là, quand vous commencez à developper votre application, comment structurer les dossiers ? Qui va où ? Comment on appelle tout ça ?
Commençons par l'organisation globale des dossiers de l'application, notamment à l'intérieur du dossier app. Le pattern qui est très globalement utilisé est d'avoir les trois dossiers principaux core , features , et shared .
Pour décrire la différence entre ces trois dossiers, je propose l'heuristique basée sur l'import et la réutilisation des éléments :
importé et utilisé dans une seule feature -> features
importé à un seul endroit, utilisé à un ou plusieurs endroits -> core
importé et utilisé à plusieurs endroits -> shared
Regardons quelques exemples.
Le principe de base de ce dossier est facile à comprendre. À l'intérieur de ce dossier, on créera un dossier par feature, et chaque dossier de feature contiendra tout ce qui lui "appartient" : les components, façades, store, services, models, interfaces… parfois même des directives et pipes si elles sont spécifiques à la feature.
La vraie difficulté de ce dossier, c'est de trouver comment séparer les features d'une application, et il n'y a pas une seule bonne réponse. Voici quelques idées pour guider votre réflexion :
commencez par des principes larges, ce qu'on appelle les Bounded Contexts en Domain-Driven Design : dans une application e-commerce, on pourrait avoir Catalogue, Gestion de commandes, et Facturation, par exemple ; essayez le plus possible de synchroniser ces définitions sur toute la stack
à l'intérieur de ces dossiers de Bounded Context, une séparation par page ou par zone d'interface peut devenir nécessaire lorsque ces dossiers se remplissent trop
On verra l'organisation interne des features un peu plus bas.
Dans ce dossier, on retrouve les éléments du cœur de l'application :
les services transverses comme des services de logging, de notification, d'ouverture de dialog, de gestion d'erreur… en gros, n'importe quel service qui n'appartient pas à une seule feature (tout ce qui est service d'authentification/autorisation peut être placé ici OU dans une feature auth indépendante)
les intercepteurs HTTP globaux, y compris les intercepteurs d'auth si vous n'avez pas une feature auth à part
les guards et resolvers globaux
les components de layout de l'application, tout ce qui est lié à la mise en page globale
les models/interfaces partagés à travers l'application
Le dossier shared, comme son nom l'indique, contient tout ce qui est partagé à travers l'application. La nuance avec le dossier core, selon moi, est que les éléments shared sont importés à différents endroits à l'intérieur de l'application :
les components réutilisables et éléments d'UI
les directives partagées
les pipes partagées
les models/interfaces liés aux éléments shared (les objets de configuration de components partagés, par exemple)
Ces listes ne sont pas exhaustives : le but de les partager ici est de vous aider à développer vos propres heuristiques et à faire vos propres choix.
Cette structure ne s'applique pas qu'aux dossiers : elle définit aussi la direction des dépendances.
corene peut dépendre que de core— si vous vous retrouvez à avoir besoin d'un fichier qui se trouve ailleurs, il faut remettre en question votre structure
sharedpeut dépendre de sharedet decore— des interfaces définis dans core , par exemple, peuvent être utilisés dans un élément shared sans problème, mais un componentsharedne doit jamais contenir un component venant d'une feature, par exemple
feature-A peut dépendre defeature-A, de shared, et decore, mais pas de feature-B— si vous vous retrouvez avec des interdépendances entre features, soit il faut remettre en question vos frontières de feature, soit il faut extraire la dépendance et le placer dansshared
Voilà pour les dossiers principaux ; passons maintenant à la structure interne d'un dossier de feature.
OK, vous avez défini vos bounded contexts et vous allez commencer à travailler sur votre première feature.
Mais… comment structurer mon dossier ?
Le plus simple est de ranger les choses par leur rôle : components, directives, services, store…
Pour les components, je suggère d'avoir une structure de dossiers qui reflète la structure de l'interface. Si, par exemple, vous avez un component EmployeeList qui contient des components EmployeeListItem qui, eux, contiennent des components EmployeeAvatarComponent, EmployeeOverviewComponent, EmployeeActionsComponent, ça pourrait une structure ainsi :

Notez que les dossiers ne comportent pas tous le préfixe employee, alors que les fichiers si.
pour les dossiers, puisqu'on est à l'intérieur du dossier employee-list , le préfixe employee ajoute du bruit sans faciliter la lecture, alors qu'utiliser uniquement ce qui change — avatar , actions , overview etc — apporte toutes les informations nécessaires
pour les fichiers, il est d'usage que le nom d'un fichier qui contient une classe porte le nom de cette classe — c'est une norme, et la suivre réduit la charge cognitive
La structure finale de notre feature employee-management pourrait ressembler à ça :

Dans cette configuration, j'ai choisi de placer les tests dans un dossier séparé au niveau de la racine de la feature. Ce n'est pas la seule approche possible, mais elle permet de regrouper les tests par comportement plutôt que par fichier, et elle permet aussi de stocker tout ce qui est test data, fake, harness etc au même endroit que les tests.
"Il n’y a que deux choses vraiment difficiles en informatique : l’invalidation du cache et le choix des noms." — Phil Karlton
Depuis 2025, l'équipe Angular suggère de ne plus ajouter les suffixes Component, Service, Directive ou Pipe à vos classes, car ça alourdit considérablement le bruit cognitif sans vraiment ajouter de valeur. Donc, par exemple :
EmployeeListComponent deviendrait EmployeeList
EmployeeService pourrait devenir Employees, EmployeeData, EmployeeApi…
HighlightDirective deviendrait simplement Highlight
ShortenPipe deviendrait Shorten
Il en va de même pour les noms de fichiers : employee-list.component.tsdevient simplement employee-list.componentetc.
En TypeScript, vous avez la possibilité d'utiliser des barrel files : des fichiers, généralement nommés index.ts , qui ne font que des "ré-exports". L'intérêt de ces fichiers est de simplifier les imports, généralement en regroupant toutes les classes d'un dossier.
Par exemple, imaginez le dossier suivant :

Si, dans un service, j'ai besoin de ces quatre interfaces, les imports ressembleront à ceci :
import { Employee } from '../models/employee';
import { Manager } from '../models/manager';
import { Position } from '../models/position';
import { Salary } from '../models/salary';Cependant, si on crée, dans le dossier models , un fichier index.ts comme ceci :
export * from './employee';
export * from './manager';
export * from './position';
export * from './salary';Les imports du service deviennent donc :
import { Employee, Manager, Position, Salary } from '../models';Les imports sont ainsi moins verbeux et plus lisibles.
Vous avez certainement déjà rencontré ce genre de cas de figure :
import { AuthConfig, Permission, User } from '../../../core/auth/models';Ici, on a bien utilisé un barrel file pour n'avoir qu'un seul import, mais cet import relatif où il faut remonter de trois niveaux pour ensuite redescendre dans core ajoute beaucoup de bruit et peut rendre plus difficiles les refactorisations.
Pour des dossiers aussi sollicités que core ou shared , vous pouvez utiliser l'option paths dans votre tsconfig pour générer des alias. Pour l'exemple ci-dessus, dans le tsconfig.json à la racine du projet, on peut ajouter l'élément suivant aux compilerOptions :
"paths": {
"@core/*": ["src/app/core/*"],
}Ce qui permet de transformer l'import problématique :
import { AuthConfig, Permission, User } from '@core/auth/models';C'est beaucoup plus lisible ! On peut même ajouter un barrel file dans le dossier auth qui ré-exporte le dossier models :
// core/auth/index.ts
export * from './models';L'import en est encore simplifié :
import { AuthConfig, Permission, User } from '@core/auth';Vous pouvez faire de même pour le dossier shared afin que les imports dans votre application soient propres et lisibles !

Vous êtes en plein audit d'une application Angular. L'équipe de votre client vous demande spécifiquement de questionner la structure du projet.
Choisissez un de vos projets récents (ou, à défaut, trouvez un projet open-source sur GitHub)
Avec les différentes approches et techniques proposées ici, notez les endroits où vous pensez qu'une restructuration serait bénéfique, et pourquoi.
Organisez votre application aveccore,shared, etfeatures, en respectant strictement les directions de dépendances entre eux.
Rangez les éléments par rôle tout en reflétant la hiérarchie de l'interface utilisateur dans l'organisation des composants, et créez des sous-features dès qu'un dossier devient trop volumineux.
Supprimez les suffixes Component, Service, Directive des noms de classes et fichiers pour réduire le bruit cognitif
Utilisez des barrel files (index.ts) et des TypeScript paths (@core/*,@shared/*) pour créer des imports propres et lisibles
Cette approche de structuration fonctionne parfaitement pour des applications de taille moyenne, mais que faire lorsque votre écosystème Angular grandit ?