Préface

Le but de ce document est d'aider à concevoir et à développer plus efficacement, plus rapidement et avec moins de risques, des applications web de qualité dans un environnement .NET. Pour atteindre ce but, ce document définit une méthodologie, des principes d'architecture et de règles de conception à suivre.

L'approche Agile

La méthodologie de gestion de projet est la clé de voûte de tout projet. Elle régit le succès du projet. C'est pourquoi, il est indispensable d'adopter une méthodologie adaptée.

Utilisez l'approche Agile.
L'approche Agile a été établie autour de 4 valeurs fondamentales:

  • Les individus et leurs interactions
  • Délivrer des logiciels opérationnels
  • La collaboration avec les clients
  • L’adaptation au changement

Une liste de 12 principes de travail a pu être établie à partir de ces valeurs fondamentales. Ces principes sont le coeur de l'approche Agile.

L'approche agile a donnée naissance à différentes méthodologies; Extreme Programming, Scrum, Kanban.
Utilisez la méthodologie Scrum.

Concrètement, la méthodologie Scrum utilise un principe de développement itératif et incrémental.
Chaque itération est définie avec le client.
Chaque itération détaille les fonctionnalitées à développer en les ordonnant par priorité.
Par itération, un macro planning est établi afin de répartir les tâches à réaliser.
Au terme de l'itération, un délivrable est fourni au client.

image Agile Methodology

Documentation: Agile Principles and Values
Documentation: Introduction aux méthodes agiles et Scrum

Utilisez les outils Agile fourni par Visual Studio Team Services pour gérer les différents aspects liés à la gestion du projet.
Documentation: Create your backlog.
Documentation: Agile Planning and Iterations (TFS)

Architecture Overview

L'architecture applicative a un impact direct sur le succès global de l'application. En effet, les décisions prises lors de la conception et du développement de l'application vont avoir un impact considérable sur la fiabilité, la perfomance et la sécurité de l'application. Il est donc crucial de choisir une architecture adaptée.

Le but d'une architecture applicative est de définir une solution structurée répondant à toutes les exigences techniques et opérationnelles tout en optimisant la maintenabilité, la performance et la sécurité.
D'un point de vue technique, cela signifie qu'il faut utiliser les library choisies et organiser son code en suivant un ensemble de règles strictes propres aux modèles programmatiques choisis.

Toutes les recommandations faites ont été pensées en ce sens et peuvent se résumer en quelques points:

  • Utilisez l'architecture en couche.
  • Partitionnez l'application en 3 couches; une couche de présentation, une couche métier et une couche d'accès aux données.
  • Utilisez le pattern MVC pour la couche de présentation.
  • Utilisez le pattern Repository pour centraliser les règles d'accès et la logique d'interaction avec les données.
  • Utilisez le pattern Unit of Work pour gérer le contexte transactionnelle c'est-à-dire pour gérer un ensemble d'opération de saisie ou de consultations de données.
  • Utilisez le pattern O/RM pour gérer l'interaction avec les données sous forme d'objets programmatiques.
  • Utilisez le pattern IoC & DI pour minimiser le couplage entre les couches.
image architecture

Schéma alternatif: Vue d'ensemble d'une application web moderne.
Documentation: Microsoft Application Architecture Guide.
Documentation: Application Architecture Knowledge Base.

Les différentes recommandations architecturales applicatives citées sont détaillées dans la suite de ce document.

Architecture Patterns

Les modèles de conception décrites ci-dessous ne sont pas spécifique à une couche.

Inversion Of Control

L'inversion de contrôle (IoC) est un desing pattern qui interdit aux objets d'instancier explicitement des dépendances à d'autres objets pour réaliser leur travail. Le but de l'inversion de contrôle est de réduire le couplage entre les classes.
L'injection de dépendances (DI) est une implémentation du pattern IoC. Concrètement, pour implémenter l'injection de dépendance, on définit une interface de la classe agrégat comme champ d'instance. Ce champ sera ensuite affecté par référence.
Documentation : The Dependency Injection Design Pattern
Documentation: Inversion of Control.
Documentation: Dependency Injection.

Utilisez la library unity pour implémenter le pattern d'injection de dépendances.

Unit Of Work

Le pattern Unit of Work est utilisé pour gérer un contexte transactionnel partagé entre tous les repository. Concrètement, l'Unit Of Work garde en mémoire les modifications (ajout, mise à jour et supression). Ces modifications sont, ensuite, regroupées en une transaction unique (unité de travail) et soumises au système de stockage dans le but d'être persistées. Le but du pattern Unit of Work est de s'assurer qu'un ensemble de modifications réussissent ou soient avortées proprement.

image uow

Documentation : Unit of Work Design Pattern.

L'architecture en couche

L'architecture en couche regroupe logiquement un ensemble de fonctionnalités en couches distinctes. Ces couches sont empilées verticalement les unes sur les autres. Les composants d'une couche peuvent interagir uniquement avec les composants de la même couche ou les composants de la couche directement inférieure. Grâce à cette structure, les couches sont faiblement couplées.
En conclusion, cette séparation en couche aide à respecter une séparation nette des aspects (SoC) favorisant ainsi la flexibilité et la maintenabilité du code.

Avantages

Utiliser une architecture en couche offre les avantages suivants:

  • Maintenabilité: La separation of concern aide à organiser le code en fragment plus facilement maintenable.
    Par exemple, si demain un site web devait être remplaçé par une application de bureau, seule la couche de présentation devrait être impactée. De même, si demain la base de données change, seule la couche d'accès aux données devrait être impactée.
  • Evolutivité: Le separation of concern permet de faire évoluer chaque fragment plus aisément et indépendemment des autres couches.
  • Performance: Déployer les couches sur différent niveaux physiques peut améliorer la mise à l'échelle en fonction des besoins, la tolérance aux pannes matérielles et la performance applicative.
  • Réutilisabilité: La separation of concern permet de réutiliser les couches au sein d'autres applications.
  • Testabilité: Chaque couche peut etre testée séparement au travers de test unitaire par exemple.
  • ...

Pour une application web, on distingue 3 couches distinctes:

*Exception, lorsque la logique métier est faible voire inexistante, la couche métier est, alors, optionnelle. Dans ce cas, le code logique sera implémenté dans le controller correspondant de la couche de présentation.

image architecture

Documentation: Layered Architectural Style
Documentation: Layered Application Guidelines

La couche de présentation

La couche de présentation gére les interactions utilisateurs. L'utilisateur interagit avec le système au travers des interfaces graphiques. En d'autres mots, cette couche est le pont entre l'utilisateur et la logique métier encapsulée dans la couche métier.

Utilisez le modèle de conception MVC dans l'implémentation de la couche de présentation. Le modèle architecturale MVC sépare l'application en 3 composants principaux: le modèle, la vue et le controller. La logique UI est définie dans la vue. La logique de saisie des données est définie dans le controller. La logique métier est définie dans le modèle.

Avantages

  • Maintenabilité: Le modèle architectural MVC (Model-View-Controller) sépare l'application en trois composants principaux : le modèle, la vue et le contrôleur. Cette Seperation of concern (SoC) produit un code clair et organisé. Cette organisation structure le code et unifromise les développements. Par conséquent, le code est plus facilement maintenable.
  • Evolutivité: De nouveau grâce à cette Separation of concerns (SoC), il est possible de faire évoluer l'un de ses composants indépendamment des autres.
  • Granularité: MVC permet d'avoir un controle total sur le code.
  • UI & UX friendly: L'intégration d'interfaces utilisateur riches est facilitée par un contrôle total de la vue produite. Autre point, la problématique multi onglets récurrentes offrant une expérience ultisateur améliorée est résolue puisque MVC est stateless.
  • Testabilité: Avec MVC, il est facile de tester une application. Par conséquent, il est possible de faire du Test Driven Development (TDD).
  • L'optimisation pour les moteurs de recherche (SEO): Le système de routage intégré à ASP.NET MVC favorise naturellement un meilleur classement dans les moteurs de recherche.
  • Développement en parallèle: L'organisation claire offerte par MVC facilite les développements concurrents.

image architecture

Documentation: ASP.NET MVC Overview
Documentation: Presentation Layer Guidelines

Pour développer la couche de présentation, respectez les points suivants:

  • Utilisez l'ASP.NET MVC Framework.
    Référez vous au site officiel.

Pour développer les interfaces utilisateurs, respectez les points suivants:

  • Structurez les interfaces graphiques avec le HTML.
    Réfèrez vous aux standards Html.
  • Stylisez les interfaces graphiques via le CSS.
    Réfèrez vous aux standards Css.
  • Dynamisez les interfaces graphiques avec le Javascript.
    Réfèrez vous aux standards Javascript.
  • Utilisez le framework Bootstrap pour la partie front-end.
    Réfèrez vous au site officiel.

Pour développer une Rich Internet Application, respectez les points suivants:

  • Utilisez le framework AngularJS.
    Référez vous au site officiel.

Restez attentif aux points suivants:

  • La Gestion des exceptions: Les messages d'exceptions doivent être redéfinis afin d'avoir un message clair et compréhensible par l'utilisateur.
  • La Validation: Pour une application web, réalisez la validation cliente avec le script jquery.validate.js.
    Pour une application web riche, réalisez la validation au travers des fonctions de validation du framework client (angular).

    Double Validation (client et serveur)

    Réalisez toujours une double validation des entrées utilisateurs; une validation côté client et une validation côté serveur.
  • L'authentification: L'authentification est l'un des points critiques de sécurité. Utilisez l'ASP.NET Identity pour gérer l'authentification au sein d'une application ASP.NET MVC.
  • L'autorisation: L'autorisation est l'un des points critiques de sécurité. Utilisez l'ASP.NET Identity pour gérer l'autorisation au sein d'une application ASP.NET MVC.
  • Le système de cache: Le caching est l'une des clés pour accroitre la performance d'une application web. L'ASP.NET MVC framework inclut une mécanique de gestion du cache.

La couche métier

La couche métier centralise les règles métiers et implémente les fonctionnalités centrales de l'application.

Pour développer la couche métier, respectez les points suivants:

  • Utilisez le pattern façade dans les applications complexes. Le composant façade fournit une interface simplifiée des méthodes accessibles rendant plus facile l'utilisation de la logique métier.

    Le composant facade est optionelle

    L'implémentation du pattern façade est optionelle. En effet, la balance entre temps, effort et gain est négligeable dans les applications où la complexité métier est faible à modérée. Toutefois dans les applications complexes, l'utilisation du composant façade est bénéfique. En conclusion, l'implémentation du composant façade est contextuel à chaque projet.

image architecture

Documentation : Business Layer Guidelines

Restez attentif aux points suivants:

  • La gestion des exceptions: Les messages d'exceptions doivent être redéfinis et ne doivent pas communiquer d'information technique susceptible de faire connaitre l'architecture système utilisée.
  • La journalisation: Appliquer une bonne stratégie de journalisation est critique pour la sécurité et la fiabilité de l'application. Réferez vous au paragraphe Logs.
  • Le système de cache: Le caching est l'une des clés pour accroitre la performance d'une application web.

La couche d'accès aux données

La couche d'accès aux données permet de récupérer et de stocker les données de l'application. Les données peuvent être lues et persitées sur différents supports; une base de données relationelle, une base de données non relationelle, un fichier, ....

Utilisez un O/RM pour implémenter la couche d'accès aux données.
Un O/RM est une couche d'abstraction de haut niveau dont le but est d'interagir avec les données de l'application indépendamment de système de stockage utilisé. Concrètement, l'O/RM modélise le modèle de l'application sous forme d'objets programmatique offrant ainsi la possiblité au développeur de travailler avec les données en orienté objet.

Utilisez des Entités POCO

L'O/RM permet de modéliser le modèle de données sous la forme d'objets programmatiques personnalisés. Ces objets personnalisés sont par définition des entités. Si on se réfère au paragraphe sur la stratégie de communication, ces objets personnalisés doivent être des objets POCO.

Avantages

  • Maintenabilité: Le code se focalise sur les développements propre à l'application. Le code d'accès aux données est centralisée dans une DLL.
  • Interopérabilité: Moteur de query indépendant du système de stockage.
  • Testabilité: Il est facile de créer des tests validants les requêtes.
  • Vitesse de développement: LINQ to entities fournit une autocomplétion et une validation à la compilation. Autogénération du code.
  • Flexibilité: Il est possible de travailler avec un ORM en gardant la possibilité d'utiliser des procédures stockées lorsque cela est nécessaire.

Couplez l'O/RM avec le pattern Repository.
Documentation: The Repository Pattern.

Avantages

  • Maintenabilité: Le code pour interagir avec les données est centralisé. La quantité de code nécessaire est également diminué. Ces 2 facteurs favorisent la maintenance de l'application.
  • Testabilité: Le pattern repository facilitent l'exécution des tests unitaires et par conséquent, il permet de tester l'interaction avec le modèle de données de l'application.
  • Flexibilité: Le pattern repository fournit une achitecture flexible qui peut facilement s'adapter.

Utilisez le pattern Unit of Work pour gérer le context transactionelle.
Documentation: Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application.

Avantages

  • Maintenabilité: Respect des principes de programmation SOLID.
  • Intégrité: Respect l'intégrité du modèle de données en s'assurant que l'ensemble des modifications soient correctement persitées ou avortées.

image architecture

Documentation : Data Layer Guidelines

Pour développer la couche d'accès aux données, respectez les points suivants:

Restez attentif aux points suivants:

  • La journalisation: Appliquer une bonne stratégie de journalisation est critique pour la sécurité et la fiabilité de l'application. Réferez vous au paragraphe Logs.
  • Le système de cache: Le caching est l'une des clés pour accroitre la performance.

La couche transversale

La couche transversal contient les élements communs aux autres couches.
Cette couche s'occupe de définir:

Documentation : Crosscutting Concerns

La stratégie de communication

Utilisez des objets POCO pour communiquer entre les différentes couches.

N'utilisez pas le pattern DTO

Les DTO sont des containeurs de données. Le but d'un objet DTO est de véhiculer les états d'un objet. Dans une architecture multi-couches, le pattern DTO est utilisé pour communiquer entre les couches tout en offrant un découplage entre les couches et les objets métiers.

Dans l'architecture proposée, Les objets POCO jouent le même rôle et offrent au minimum les mêmes avantages que les objets DTO. Par conséquent et dans ce contexte, utiliser le pattern DTO ne ferait que complexifier le code et le développement.

En conclusion, si les différentes recommandations architecturales applicatives faites dans ce document sont appliquées, le pattern DTO ne doit pas être utilisé.

Best practices

Principes de programmation

Lorsque vous développez, respectez les principes de programmation suivant :

  • Separation of Concerns (SoC): Organisez logiquement votre code pour maximiser la réutilisabilité et la maintenabilité du code.
  • SOLID Principles

    Documentation: Principles of Object-Oriented Design.
    Documentation: Applying S.O.L.I.D. Principles in Microsoft .NET/C#.

    • The Single Responsability Principle (SRP): une classe doit avoir une et une seule responsabilité.

      A class should have one, and only one, reason to change.

    • The Open/Closed Principle (OCP): Une classe, une méthode, un module doit pouvoir être étendu, supporter différentes implémentations sans pour cela devoir être modifié.

      You should be able to extend a classes behavior, without modifying it.

    • The Liskov Substitution Principle (LSP): Les sous-types doivent être remplaçables par leur type de base.

      In a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties, such as correctness, of that program.

    • The Interface Segregation Principle (ISP): Les clients d'une entité logicielle ne doivent pas avoir à dépendre de méthodes qu'ils n'utilisent pas.

      No client class should be forced to depend on methods it does not use.

    • The Dependency Inversion Principle (DIP):
      • Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau. Les deux doivent dépendre d'abstractions.
      • Les abstractions ne doivent pas dépendre des détails. Les détails doivent dépendre des abstractions.
      • High level modules should not depend upon low level modules. Both should depend upon abstractions.
      • Abstractions should not depend upon details. Details should depend upon abstractions.
  • DRY: Ne répétez jamais le même code plus d'une fois afin de gagner en réutisabilité, testabilité et en maintenabilité.

    Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

    DRY Principle

Commentaires

Commentez le code lorsque cela est nécessaire. Utilisez les commentaires pour expliquer le code; lorsque celui-ci est complexe, lorsque qu'une correction a dû être apportée, etc.

Documentez le code à l'aide des commentaires de documentation XML.

Documentez le code source

Un code documenté est un code plus facilement maintenable.
Chaque méthode doit être documentée.
Chaque classe doit être documentée.

Documentation : XML Documentation Comments.

Logs

Les logs sont des fichiers journaux qui enregistrent l'historique des événements d'un processus. Les logs peuvent être enregistrés en mémoire, en base de données ou en fichier texte.

Journalisez, au minimum, les exceptions de type Error et Critical.
Privilégiez les logs au format .svclog.

La stratégie de journalisation est propre à chaque application

La stratégie de communication est contextuelle à chaque projet. En d'autres mots, chaque application doit déterminer ce qui est important de journaliser.

Tests

Il est indispensable de tester une application. En effet, les tests sont primorodiaux pour s'assurer du bon fonctionnement d'une application. Il existe différents types de tests qui peuvent être créés selon le projet. Concevez, au minimum, des tests unitaires et des tests d'acceptation quelque soit le projet.

Les tests sont conçus en phase d'analyse

Collaborer avec le client est indispensable pour concevoir un test d'acceptation valable. De plus, chaque fonctionnalité présente des tests qui lui sont spécifiques. Par conséquent, les tests d'acceptation doivent être établi lors de la phase d'analyse.

Tests unitaires

Les tests unitaires testent chaque pièces de code, appellée "units", individuellement et indépendamment. Les tests unitaires sont implémentés à la fin de chaque itération. Les tests untaitres doivent être exécutés avec succès afin de valider le développement.
Un test est unitaire lorsque :

  • Il ne communique pas avec la base de données.
  • Il ne communique pas avec d’autres ressources sur le réseau.
  • Il ne manipule pas de fichier.
  • Il peut s’exécuter en même temps que les autres tests unitaires.
  • Il ne doit rien faire de spécial pour s'exécuter, comme éditer un fichier de configuration.

Tests d'intégration

Si plusieurs parties de code sont testées ensemble, il s'agit alors de tests d'intégrations. Les tests d'intégration et les tests unitaires ont un but différent. L'objectif premier lorsque l'on test est de valider chaque pièces de code individuellement afin de détecter le plus précisement possible les erreurs potentielles.

Utilisez xUnit pour réaliser les tests unitaires C#.
Documentation : xUnit
Documentation: Unit Testing in ASP.NET MVC Applications.
Documentation: Testable MVC - Building Testable ASP.NET MVC Applications.

Utilisez Karma/Jasmine pour réaliser les tests unitaires javascript.
Pour facilitez l'intégration des tests au sein de Visual Studio, vous pouvez installer l'extension Chutzpah.
Documentation : Karma et Jasmine
Documentation : Chutzpah

Intégration continue des tests

Il est possible d'éxécuter automatiquement des tests à partir du processus de génération. Plus plus de détails à ce sujet, référez vous à l'article suivant : Exécuter des tests dans votre processus de génération

Tests d'acceptation

Les tests d'acceptation s'assurent que les fonctionnalités correspondent aux besoins métiers. En d'autres mots, Les tests d'acceptation testent que l'application fonctionne de la manière attendue par le client.

Les tests d'acceptation peuvent être automatisés par les tests de type e2e. En effet, les tests e2e servent à valider un scénario. Concrètement, les tests e2e simulent une vrai interaction utilisateur en interagissant avec l'application au travers des interfaces graphiques. Le but des end to end test est de valider toutes les couches de l'application en exécutant des actions utilisateurs.

Utilisez Selenium/Protractor pour exécuter les tests end to ends javascript.
Documentation : Selenium
Documentation : Protractor

Dépendances

Gérez les dépendances d'un projet au travers de package lorsque cela est possible.

Avantages

  • Simplicité: Il est très facile d'intégrer d'installer, mettre à jour ou de supprimer un package.
  • Efficacité: Les packages sont centralisés aux mêmes endroits.
  • Maintenabilité: Les packages sont versionnés. Chaque projet intégrant un package peut alors décider qu'elle version utilisé et à quel moment mettre à jour.

Package Manager

Pour gérer efficacement les packages, utilisez un package manager. Le package manager est un outil de gestion de packages. En d'autres mots, il automatise le téléchargement, l'installation, la mise à jour et la désinstallation de packages au sein d'un projet.

Utilisez NuGet pour gérer les packages serveurs.

Documentation : NuGet

Un NuGet d'entreprise

Il est possible de créer et publier ses propres packages. Pour plus de détails à ce sujet, réfèrez vous à cette article: Creating and Publishing a Package. En d'autres mots, il est possible de gérer les dépendances aux library internes avec un Nuget propre à l'entreprise. Cette solution offre tous les avantages d'un gestionnaire de package tout en fournissant une solution simple et efficace aux gestions des dépendances. En conclusion, il est recommandé de gérer les dépendances internes avec un NuGet d'entreprise.

Déploiement

Utilisez l'option de déploiement (one-click publish) de Visual Studio pour déployer les applications web sur les différents environnements.

Profile de déploiement

Vous pouvez paramétrer le déploiement au travers des profils de publication. Il est recommandé de configurer un profil de déploiement par environnement.

Un publish profile par environnement

Chaque environnement doit avoir son propre profil de déploiement.

Un fichier de configuration par environnement

Attention lors du déploiement, restez attentif que chaque environnement possède son propre fichier de configuration.

Credentials

Certaines données de configuration, comme la chaine de connection à une base de données, nécessitent d'inclure des données d'authentifications sensibles (ip, login, mdp, ...). Vu le caractère sensible de ces informations, ces données ne doivent pas être accessibles depuis le code source de la solution. Regroupez ces données confidentielles dans un fichier externe.

Des credentials différents par environnement

Restez attentif au fait que ces informations sont généralement spécifique à chaque environnement.

Les données d'identification ne doivent pas être liées au code source

Les données sensibles d'identification ne doivent pas être liées aux sources de l'application pour des raisons évidentes de sécurité.

Notes

Gardez un esprit critique

Ce document est une synthèse.
Les recommandations définies dans ce document sont valable pour la majorité des applications web.
Toutefois, si cela s'avère nécessaire pour le succès du projet, il est important d'adapter l'architecture en fonction des objectifs à atteindre.
En cas de doute, référez vous aux ressources référencées en fin de chaque article.