Skip to content

Instantly share code, notes, and snippets.

@Yopadd
Last active August 16, 2021 09:12
Show Gist options
  • Save Yopadd/674afc34bfe4be956fe5970b8b8bb832 to your computer and use it in GitHub Desktop.
Save Yopadd/674afc34bfe4be956fe5970b8b8bb832 to your computer and use it in GitHub Desktop.
An other application structure to Angular

Structure de l'application

Utilisez le plus que possible le ng CLI pour générer les fichiers souhaités

src-┐
    assets-
    styles-
    index.html
    main.ts
    app-┐
        moduleName-┐ // ng g module <name> [--route <routeName> --module <moduleTargeted>] [--routing]
                   [components] // ng g component moduleName/components/<name> --module <moduleTargeted>
                   [pages] // ng g component moduleName/pages/<name> --module <moduleTargeted> --type page
                   [models] // ng g class moduleName/models/<Name> || ng g enum types/<name> 
                   [guards] // ng g guard moduleName/guards/<name>
                   ![services]
                   ![directives]
                   ![pipes]
                   ![moduleName]
                   [moduleName-routing.module.ts] // Créé par --routing
                   [moduleName.component.ts|html|scss|spec] // Créé par --routing
                   moduleName.module.ts
        shared-┐ // ng g module shared
               [components] // ng g component shared/components/<name> --module <moduleTargeted>
               [directives] // ng g diretive shared/directives/<name> --module <moduleTargeted>
               [pipes]-┐ // ng g pipe shared/pipes/<name> --module app
                       pipeName.pipe.ts|spec.ts
               [models]
               ![moduleName]
               shared.module.ts
        interceptors-┐ // ng g interceptors interceptors/<name> 
                     index.ts // The "barrel" file https://angular.io/guide/http#provide-the-interceptor
                     interceptorName.interceptor.ts|spec.ts
        guards-┐ // ng g guards/<name>
               guardName.guard.ts|spec.ts
        resolvers-┐
                  resolverName.resolver.ts
        services-┐ // ng g service services/<name>
                 serviceName.service.ts|spec.ts
        models-┐ // ng g class models/<Name> || ng g enum types/<name> 
               Model.ts|spec.ts
               enumName.enum.ts
        [errors]
        app-routing.module.ts
        app.component.ts|html|scss|spec
        app.module.ts

[] : optionel

![] : optionel, à utiliser avec précaution. Ne devrait pas servire dans la grande majorité des cas.

Structure des dossiers composants

Un composants n'est jamais appelé par un router.

components-┐
           componentName-┐
                         componentName.ts|html|scss|spec.ts
                         [components]
                         ![models]
                         ![services]
                         ![directives]
                         ![pipes]

Strucutre des dossiers pages

Les pages sont des composants. On les différencie cependant par un point : elles ne sont appelées que par un router.

L'arborescence des pages doit représenter au mieux l'arborescence du routing.

exemple : URL /path/to/my-page

src/app/myModule/pages/path/pages/to/pages/myPage/myPage.component.ts|html

pages-┐
      pageName-┐
               componentName.ts|html|scss|spec.ts
               [components]
               [pages]
               ![models]
               ![services]
               ![directives]
               ![pipes]

Les modules

On distingue deux types de modules.

  • Les modules avec router, ils peuvent contenir des pages et des guards.

Ce sont les modules les plus fréquents. Votre découpage de modules doit s'axer sur vos fonctionnalitées. ex : shipping, builling, address book.

  • Les modules sans router, ils ne peuvent pas contenir de pages ni de guards. Plus rare, ils se résument généralement à un module shared.

Le module shared doit contenir tous les composants, directives et pipes génériques de l'application et de fait utiliser par plusieurs modules (mais pas forcèment tous).

Pourquoi ne pas inclure des services et des guards dans le module shared ?

Les services et les guards sont de facto par @Injectable({ providedIn: 'root' }) partagés dans toute l'application.

Le module shared ne doit pas être importé dans le appModule il doit explicitement être importé dans les modules souhaités. Sinon il perd de sont intérêt au profit de appModule. Il faut développer le shared module comme s'il était une librairie externe. Ses composants doivent être agnostiques à votre métié.

Les services

Le dossier services à la racine contient tous les services de l'application avec les annotations @Injectable({ providedIn: 'root' }).

Dans le cas excpetionnel où un service est défini au un niveau d'un module, d'un component ou d'une directive, etc ... Il doit alors utiliser @Injectable({ providedIn: <Target> }) et ne doit jamais contenir 'root'. Il peut utiliser any dans le cas d'un module lazy.

Il faut utiliser autant que possible l'annotation @Injectable({ providedIn: ... }) plutôt que @NgModule({ providers: [...] }) qui reste pour des raisons de compatibilités.

Les composants

Dans un framework avec des composants l'héritage est à proscrire ! Si des composants partagent des logiques alors il faut les découper en plus de composants et/ou services et faire de la composition.

Les models

C'est dans ces dossiers que vous devez représenter tout votre système de typage, Interface, Class, Enum.
Le typage ne suit pas le graph de dépendance des NgModules mais celui des ESModules. Déclarez vos typages au plus proche des éléments qui l'utilisent.

Utilisez les Class pour représenter des objets de que vous allez manipuler dans votre métier, aux quels vous souhaitez y associer des méthodes.

Vous utiliserez les Interfaces majoritairement pour représenter des ressources que vous récupérez ou que vous envoyez. Un exemple de structure pour représenter une ressource.

interface Input { ... }

interface Output { ... }

class MyObject {
    constructor (input: Input) { ... }

    usefullMethod() { ... }
    
    toJSON: Output () { ... }
}

Les erreurs

Il est parfois utile de représenter des erreurs avec le système de typage. Elles pourraits être représentées dans le dossier models. Mais il peut être pratique de les représenter dans un dossier différent.

export class InputRequiredError extends Error {
  constructor(property: string) {
    super(`${property} is required`);
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment