Les 6 règles d`architecture pour sécuriser le développement logiciel
Transcription
Les 6 règles d`architecture pour sécuriser le développement logiciel
Les 6 règles d’architecture pour sécuriser le développement logiciel Ce document propose une démarche et des règles simples et puissantes pour créer l’architecture d’un logiciel. Sa lecture ne demande pas une technicité particulière en informatique1. Son double objectif est d’aider les architectes en leur proposant des repères, mais aussi de mieux faire comprendre aux autres professionnels ce qu’est le travail d’un architecte logiciel. Prologue L’architecture d’un logiciel Le présent document propose des règles simples, minimales et efficaces, dont le respect augmente sensiblement les chances de parvenir à une architecture logicielle saine, et au final, un développement réussi. Le domaine d’application de ces règles est très large - Ces règles s’appliquent, que le logiciel soit développé pour étendre un catalogue de produits logiciels, généralement à l’initiative d’un chef de produit, ou pour répondre à des besoins spécifiques, généralement à l’initiative d’un client. - Ces règles concernent le développement d’un logiciel ou l’évolution d’un logiciel existant. - Elles s’appliquent aux logiciels qui ont une identité autonome, et à ceux qui sont intégrés dans une solution globale aux côtés d’autres logiciels et matériels, comme dans l’informatique embarquée. Elles ont déjà été mises en oeuvre dans de nombreux développements, et par exemple: logiciels embarqués dans des équipements réseaux à très hauts débits, logiciels de sécurité, plates-formes d’administration de réseaux critiques, et aussi, systèmes d’information. Le développement d’un logiciel est une activité de conception complexe, qui inclut l’architecture comme élément structurant Le développement du logiciel intègre l’ensemble des activités de conception aboutissant à l’écriture du logiciel sous forme du code source, ce code étant le document final de conception. Note: au-delà du développement, se trouve la production proprement dite du logiciel. 1En cas de difficulté, sauter des paragraphe pour lire les lignes en caractères gras et les encadrés des règles. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 1 Celle-ci est automatisée autour du compilateur et de l’éditeur de liens. On peut distinguer plusieurs niveaux de zoom dans la conception d’un même objet logiciel, et le poids relatif de chaque niveau dépend du contexte : - L’architecture, qui consiste à décrire (1) l’organisation du logiciel en blocs de premier niveau avec leurs responsabilités et relations, (2) les principes permettant d’obtenir par leur collaboration les comportements attendus et de répondre aux contraintes techniques structurantes, et enfin, (3) les règles et choix techniques guidant leur conception détaillée. Le document d’architecture est le premier document à consulter pour comprendre un logiciel, par exemple, pour le maintenir. - La conception détaillée, qui consiste à décrire l’organisation des éléments à coder (fonctions, structures de données), à définir leurs responsabilités, et à choisir les règles et techniques guidant leur codage. - Le codage, qui aboutit au “plan détaillé” (le code source) utilisable par un environnement de production (le compilateur). A chaque niveau, des décisions sont prises. Le processus de conception a pour objectif de permettre la prise de chaque décision au bon niveau, tout en gérant dans le temps les impacts (interactions entre les niveaux, améliorations par itérations). Les règles d’architecture sont un complément efficace à une méthode de conception Le présent document prend en compte les erreurs et problématiques récurrentes auxquelles sont confrontées les concepteurs de l’architecture de logiciels. Pour y répondre, il propose des règles d’architecture. Ces règles sont focalisées sur le logiciel en lui-même, son contenu et sa structure (le “Quoi ?”). Elles sont un complément efficace à une méthodologie de conception qui décrit un processus (le “Comment ?”), mais ne s’y substituent pas. Ces règles sont énoncées pour être : ○ Simples à comprendre et à partager, et en particulier, elles sont compréhensibles sans qu’il soit nécessaire d’avoir une expertise technologique particulière ; ○ Minimales, en ce sens qu’elles sont peu nombreuses, qu’elles s’appliquent à la grande majorité des projets de développement logiciel, et que le retrait de l’une d’entre elles ouvrirait la porte à des erreurs sérieuses ; ○ Efficaces, car si une architecture est conforme à ces règles, alors il y a de grandes chances pour que le développement se fasse dans de bonnes conditions. Afin d’introduire les idées de façon plus simple, les règles sont décrites à travers un parcours fait d’une succession d’actes. Cela pourrait suggérer à tort une démarche séquentielle. En fait, c’est la méthode de conception choisie qui doit préciser l’articulation des différentes activités (notamment, l’enchaînement ou le parallélisme des activités et leur caractère itératif) et leur mode opératoire précis (en particulier, la formalisation des éléments de conception produits). Par exemple, l’acte 3 (rattacher la solution au monde réel) et l’acte 4 (mutualiser par des composants génériques) doivent être joués en fort recouvrement et en forte relation pour en tirer le meilleur bénéfice. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 2 Acte #1 S'affranchir des fausses contraintes Face à un développement à faire, il faut préciser le comportement attendu du logiciel en termes - de fonctions rendues disponibles aux utilisateurs, - de critères de qualité (par exemple: disponibilité, performances, sécurité, sûreté), - et de contraintes techniques (par exemple: choix techniques imposés par le client). Le principal écueil du développement logiciel est l’introduction de fausses contraintes Les fausses contraintes peuvent être des exigences fonctionnelles ou techniques introduites de manière implicite ou sans justification. Plusieurs phénomènes peuvent y contribuer : - Les critères de qualité de fonctionnement sont parfois formulés sans prendre compte le niveau de service réellement attendu par les utilisateurs, ou la faisabilité technique au coût objectif ; - Le besoin client est parfois traduit trop rapidement en réponses techniques, en faisant le rapprochement avec un produit sur étagère, puis en listant ses fonctionnalités, aboutissant ainsi à une lecture des besoins sous l’influence des fournisseurs ou des médias spécialisés ; - Les préconisations techniques du client sont parfois entérinées sans phase de réflexion et de dialogue, et elles induisent des contraintes fortes dans le cadre du projet ; - Des demandes clients issues d’effets de mode ; - Le besoin est parfois exprimé par quelqu’un qui projette ses propres représentations intellectuelles sans en avoir conscience, et elles peuvent induire des contraintes fortes et limitatives; - Des choix techniques historiques sont parfois reconduits alors que le contexte d’emploi du logiciel ou les technologies disponibles sur le marché ont fortement évolué. Réutiliser les compétences d’une équipe ne doit pas induire un immobilisme technique. Les fausses contraintes aboutissent à une complexité accrue du logiciel (donc un coût et un délai accru, voire à une incapacité à développer) sans avantage en terme de service rendu. On estime que 40% des échecs logiciels sont liés à ce phénomène. Il faut absolument rejeter les fausses contraintes Pour sécuriser le projet, la première chose à faire au début des travaux d'architecture est d'exclure ces fausses contraintes. Comme elle proviennent souvent de traductions "trop automatiques" des besoins en réponses techniques, il peut être utile de considérer les quelques exemples suivants. Ex 1: “Temps réel” n’implique pas “hautes performances” Face à une exigence exprimée en tant que "temps réel", la première action est de décoder la vraie signification de ce terme: - " engagement de temps " : garantie du délai d'exécution des tâches - " faible latence " : traitement rapide des événements ou des messages - " système embarqué " : la solution doit fonctionner sans opérateur Dans les années 60, le terme "temps réel" signifiait "interactif" par opposition à "différé" (batch). Un faible latence suppose de hautes performances. En revanche, un engagement de délai __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 3 ne suppose pas obligatoirement un traitement très rapide (par exemple, La Poste a un engagement d'acheminement de 48h pour une lettre, et de même, la paie se doit d’être mensuelle). De la même façon, un logiciel embarqué peut ne pas revendiquer de rapidité de calcul (par exemple, un lave-linge programmable). Ex 2: “Haute disponibilité” n’implique pas matériel ou système d'exploitation spécial De la même façon que les RAID2 fournissent une haute disponibilité de stockage par un assemblage judicieux de disques standards, une solution répartie sur des ordinateurs standards en réseau peut être plus robuste qu'une solution reposant sur un matériel ou d’un système d’exploitation spéciaux. Ex 3: “Gestion de données” n’implique pas “base de données relationnelle (SQL)” Les bases de données relationnelles permettent de faire des opérations ensemblistes sur des données organisées sous forme de tables en garantissant la cohérence des données au cours de l’exécution de lots de traitement. Dans l’informatique de gestion, et depuis les années 80, les bases de données relationnelles sont souvent utilisées comme système de gestion de données par défaut. Lorsque le problème ne relève pas de données naturellement organisées en tables, ni d’opérations sur des tables, un autre type d’organisation et de stockage des données peut être plus adapté: système de fichiers, base clé/valeur, base géographique, base colonne, base graphe. Ex 4: “Cohérence de la donnée” n’implique pas “moteur transactionnel” Un moteur transactionnel est un logiciel qui garantit la cohérence des traitements lancés en parallèle: il calcule et contrôle en un point central l’ordre d’exécution des traitements. Il privilégie ainsi la cohérence sur la rapidité de traitement et la capacité de montée en charge. Une attitude courante est d’utiliser des moteurs transactionnels systématiquement comme assurance contre le risque d’incohérence. D’ailleurs, le fait que les bases de données relationnelles intègrent souvent un moteur transactionnel est une des raisons de leur succès. D’une part, cette assurance est souvent utilisée de manière systématique et implicite alors que dans la majeure partie des cas (plusieurs lecteurs, un écrivain unique), elle n’est pas justifiée. Il convient d’identifier les réels besoins de cohérence des données et des traitements, et d’envisager l’ensemble des solutions permettant d’y répondre (gestion explicite par le logiciel par exemple) ; D’autre part, cette assurance est trompeuse car partielle. Une bonne partie des incohérences provient de défauts dans la logique de l’application et survient avec ou sans moteur transactionnel. Il convient en particulier de mettre en oeuvre en complément des mécanismes d’identification et de correction des incohérences ; Cette assurance est donc souvent inutile, toujours coûteuse, et par nature partielle. Ex 5: “SOA” n’implique pas “ESB” et “ESB” n’implique pas “SOA” Un logiciel SOA (respectant une architecture orientée service) est structuré sous forme de modules chacun fournissant des services aux autres et répondant à des principes d’interaction entre modules. Ces principes d’interaction ont un impact sur l’ensemble de l’architecture du système (décomposition en modules, interfaces entre modules). L’utilisation des produits décrits par les éditeurs comme “outil SOA” (en particulier les bus de services d’entreprise “ESB”) ne garantit pas l’application des principes SOA. Au contraire, leur utilisation sert parfois de fausse preuve du caractère SOA de l’architecture, au risque de ne pas tirer avantage de la signification véritable de SOA qui porte sur l’architecture, et non les outils. 2Redundant Array of Inexpensive Disks, gestion d’un groupe de disques comme un tout pour apporter de la résistance aux pannes et/ou de la performance __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 4 Ex 6: “Performance” n’implique pas un langage de bas niveau. Contrairement aux idées reçues, aujourd'hui, les langages de programmation à haut niveau d’abstraction (par exemple Java) sont souvent plus efficaces que les langages de plus bas niveau (par exemple C ou C++). Ils transfèrent l'optimisation du code des programmeurs aux compilateurs, et les compilateurs modernes utilisent avec plus d'efficacité les processeurs récents en exécutant de façon systématique des algorithmes d'optimisation très avancés parfois peu accessibles aux langages de plus bas niveau. Ex 7: “Service Web” n’implique pas “SOAP” et “SOAP” n’implique pas “Service Web”. Le Web repose sur des notions de navigation au sein de documents (puisqu’un clic sur un lien d’une page web permet de visualiser la page cible obtenue du serveur). Les services web étendent ces notions pour permettre l’échange de machine à machine. Plusieurs protocoles d’échange sous forme de services web existent, ayant chacun des propriétés et des qualités différentes : - Web Services SOAP (Simple Object Access Protocol) : en continuité des langages de développement objet, il permet d’exprimer la richesse métier du logiciel à l’aide de fonctions, et nécessite une gestion du catalogue de fonctions et de son mode d’emploi ; - Web Services REST (REpresentational State Transfer) : en continuité du “World Wide Web”, il permet d’exprimer la richesse métier du logiciel à l’aide d’une hiérarchie de ressources, tout en facilitant leur utilisation (interface uniforme et navigable) et assure par construction une limitation des erreurs en cas d’appels concurrents (échanges plus sûrs car sans état). Historiquement, les web services SOAP ont été plus utilisés que les web services REST, notamment parce qu’ils sont de conception plus ancienne, et qu’ils sont plus proches des concepts utilisés par les développeurs d’applications classiques. Néanmoins, lorsque la croissance itérative du nombre de fonctions et la capacité de montée en charge sont des enjeux clés, les web services REST peuvent constituer une meilleure réponse. Règle #1 L’architecture doit exclure les fausses contraintes, souvent issues d’une traduction trop rapide des besoins en choix techniques, car elles peuvent être à l’origine de dangereuses décisions d’architecture. Pour vérifier la conformité à cette règle - L’équipe qui répond à une demande client doit écrire la liste des contraintes techniques qu’elle estime devoir prendre en compte. - Pour chaque contrainte de cette liste, l’équipe doit écrire en quoi cette contrainte est justifiée. - Au final, cette liste doit avoir été challengée et validée par les équipes de direction technique et de direction du projet, et par le client lorsque cela est possible. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 5 Acte #2 Rechercher une découpe saine et robuste Pour développer un logiciel de taille importante, on est conduit à le découper en parties, pour le rendre maîtrisable et développable par plusieurs équipes. Quand chaque partie du logiciel a un rôle bien défini et une certaine autonomie, on parle de découpage en “modules logiciels”. Cette “modularité” a des avantages majeurs: - elle permet d’augmenter la qualité du logiciel, car quand le logiciel est plus clair, ses défauts sont plus facilement localisés, identifiés et traités, - elle permet de mieux planifier, gérer et contrôler les activités de conception (spécification, architecture, conception, codage, test, intégration). Techniquement, la forme que peut prendre un module est très variable: - un ensemble de fonctions travaillant dans un même domaine - une ou plusieurs classes au sens des langages objets (C++, Java) regroupant naturellement des fonctions et des données ; - un “sous-logiciel” autonome que l’on démarre ou arrête et qui communique avec les autres, par exemple en Web Services... On découpe le logiciel en modules pour le maîtriser en divisant la complexité Décomposer en modules permet de diviser la complexité en plusieurs points à traiter: - au niveau global, choisir les principes d’interaction entre modules qui répondent aux contraintes fonctionnelles et techniques structurantes, tout en étant simples. - au niveau de chaque module, l’activité de conception doit être focalisée sur les attentes vis à vis de ce module et les contraintes qu’il doit respecter. La conception d’une architecture modulaire commence donc par la recherche d’un optimum assurant la maîtrise globale du système d’information par une découpe en modules à forte cohésion interne et faible couplage externe. Pourquoi parler d’optimum? Parce que selon la taille des modules et les responsabilités que l’on donne à ces modules, on obtient une complexité totale différente: - trop peu de modules (voire un seul!), et l’on ne tire pas partie de la modularité et l’on se retrouve avec des modules trop complexes et peu maîtrisables - trop de petits modules peut conduire à une simplicité interne aux modules, mais en projetant la complexité au niveau global - un découpage inapproprié peut engendrer trop d’interactions entre modules, et des interactions trop complexes pour être bien comprises et maîtrisées. Tout au long du travail de découpage en modules de l’idée initiale à ses remises en cause par améliorations successives, on peut évaluer la qualité de la décomposition en modules. Pour cela, on déroule les principaux cas d’utilisation du logiciel et on vérifie que les besoins fonctionnels sont bien pris en compte, que les contraintes techniques structurantes sont identifiées et que des réponses y sont apportées, et que les interactions restent d’une complexité maîtrisable et maîtrisée. Ainsi, par une succession de choix et améliorations, on arrive à une architecture satisfaisante. Cette démarche intellectuelle sous-tend la plupart des méthodes d’architecture logicielle. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 6 Celles-ci se différencient par l’organisation dans le temps des différentes activités d’ingénierie, et le formalisme des documentations associées. Si des éléments de cette démarche peuvent se retrouver hors du domaine du logiciel, l’activité d’architecture logicielle présente des caractères spécifiques. La suite donne pour cela quelques bons principes de conception d’une architecture logicielle modulaire. Chaque principe est expliqué afin de faciliter son utilisation de manière pertinente, projet après projet. On définit des modules intelligibles pour simplifier le développement logiciel La définition d’un module (les fonctions qu’il assure, les données qu’il gère, et ses modalités d’utilisation) doit privilégier la cohésion et l’intelligibilité. Un module dont on peut facilement expliquer, entendre et comprendre la responsabilité est plus facile à concevoir, développer et maintenir. Regrouper dans un module un ensemble cohérent de fonctions centrées sur un seul sujet permet également de simplifier et rendre plus efficace son intégration et son utilisation au sein d’une solution. Une brique de Lego à forme simple est plus facile à placer qu’une brique à dessin complexe. “Définition facile à donner”, “un seul sujet”, voilà qui rappelle les bons conseils de Boileau dans son “Art Poétique” : “Ce que l'on conçoit bien s'énonce clairement, Et les mots pour le dire arrivent aisément” (chant I) ... “Qu'en un lieu, qu'en un jour, un seul fait accompli, Tienne jusqu'à la fin le théâtre rempli.” (chant III) Chaque module doit regrouper données et fonctions en rapport Si des données sont modifiées par plusieurs modules, les conséquences peuvent être graves : - difficulté à identifier la source du problème si ces données ont des valeurs incohérentes ; - difficulté à assurer la sécurité de ces données ainsi partagées ; - risques de conflits avec plusieurs modules accédant en même temps aux mêmes données; - difficulté pour évaluer ce qu’il faut changer dans les différentes parties du logiciel si on fait évoluer la structure des données. Pour éviter ces inconvénients, les données, telles que fichiers ou bases de données ne doivent pas être partagées entre plusieurs modules, mais être intégrées dans des modules responsables de leur gestion. Chaque module contient des données, dont il est responsable au plan de l’intégrité et de la sécurité. Il est le seul à pouvoir y accéder directement. Un module est donc le regroupement: - de données passives (nombres ou tableaux, textes, fichiers, tables de bases de données...) - de fonctions qui, pour rendre des services, peuvent lire ou modifier ces données. Ni les données passives, ni même leur structure, ne sont visibles directement de l’extérieur. Les données d’un module ne peuvent être modifiées que par les fonctions de ce même module. Celles-ci sont en nombre limité et sont normalement écrites avec le souci de __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 7 préserver l'intégrité et la cohérence des données. Ce confinement des données améliore la qualité. En particulier, si les données d’un module prennent des valeurs anormales ou incohérentes, il est normal de suspecter le module: - le codage du module est-il défectueux? - a-t-il accepté une demande qu’il aurait dû refuser? Pour prévenir cette possible corruption des données d’un module, - les fonctions du modules doivent systématiquement contrôler les appels reçus (droits/ sécurité, validité de forme et de fond des paramètres) avant d'exécuter des actions susceptibles de compromettre la cohérence des données - le module doit mettre en oeuvre des fonctions de détection et d’auto-réparation des incohérences de ses données. Les interactions entre modules peuvent être simples grâce à la conception orientée service Afin d’assurer la simplicité et l’évolutivité des interactions entre modules, une bonne pratique consiste à les définir en respectant l’esprit des principes d’architecture SOA : 1.absence de partage entre modules : chaque module a la pleine responsabilité de son état, et en particulier, de ses propres données ; 2.collaboration sous forme d’appel de services : les modules interagissent par le biais d’appels respectant des principes de couplage lâche, c’est-à-dire des appels consistants (chaque appel forme un tout), sans état (par de relation implicite entre deux appels), et indépendants de la technologie de développement (les partenaires de l’appel n’ont pas à partager la même technologie) ; 3.facilité d’utilisation et de combinaison des services : les services offerts par les modules sont définis explicitement, simples (intelligibilité, facilité d’emploi) de manière à être facilement composés pour réaliser des fonctions. Ce sont ces principes qui font l’apport essentiel de SOA en matière d’idées sur l’architecture. Au-delà de ces principes, un certain nombre de technologies permettent de faciliter la conception orientée service: - pour les appels locaux (c’est-à-dire entre modules sur une même machine): Avec les langages de programmation orientés objet, on peut choisir de réaliser un module comme une ou plusieurs classes d’objets, et les appels entre modules comme appels de fonctions. Le résultat n’est pas strictement conforme à l’esprit, car faire un appel direct de fonction suppose que les fonctions soient dans le même environnement technique (“couplage fort”), mais il permet de mettre en oeuvre les principes d’interaction SOA; - pour les appels à travers un réseau: Les services web constituent un bon moyen d’outiller simplement la mise en œuvre de la conception orientée service. Parmi les styles de définition de services web, REST facilite l’évolution du logiciel de par sa simplicité d’interaction, ses propriétés de navigabilité et de croissance par incréments. On notera qu’une conception de logiciel basée sur des modules capables de communiquer en réseau est une bonne base pour tirer profit du "cloud computing" car on va pouvoir répartir les modules sur plusieurs serveurs pour utiliser aux mieux ces machines. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 8 La modularité doit accompagner les contraintes techniques non fonctionnelles Les contraintes non fonctionnelles (robustesse, sécurité, sûreté, performances...) doivent être propagées dans les différents modules de manière intelligible. Par exemple... Chaque module doit contribuer à la robustesse du logiciel L’architecture d’un logiciel fait apparaître en général des modules spécialisés dans la gestion de la robustesse du logiciel (supervision, démarrage/arrêt, sauvegarde/restauration). Cependant, et en complément de ces modules, l’ensemble des modules de la solution doit prendre en compte l’objectif de robustesse. A l’intérieur de chaque module, on doit mettre en oeuvre des moyens internes permettant de diminuer les conséquences des erreurs de fonctionnement et des pannes internes au module ou impactant son environnement. Ces moyens internes peuvent comprendre : - des modes de fonctionnement “dégradés” ou “à minima”, permettant de réagir aux défaillances de composants logiciels ou matériels; - un système de gestion d’exceptions permettant de réagir aux erreurs d’exécution du logiciel; - un système de gestion de la sauvegarde (par gestion de journaux ou de points d’arrêt) et de la restauration du module. Entre les modules, un bon principe permettant d’augmenter la robustesse du logiciel est d’augmenter la souplesse des interfaces. Ce principe a été énoncé par John Poster et est entré dans la norme Internet RFC761 comme principe de robustesse sous la forme suivante : “Soyez conservateur dans ce que vous envoyez; soyez libéral dans ce que vous acceptez”. - chaque module doit être conçu de façon à ce qu’il envoie soit le plus strictement possible conforme à ce qui est attendu, et le moins susceptible de poser des problèmes au récepteur, - en sens inverse, le récepteur doit être capable de comprendre les messages qu’il reçoit de façon la plus tolérante possible. Chaque module doit contribuer à la sécurité du logiciel Au sein d’un système d’information, la sécurité des accès vise à assurer que les traitements et les informations ne sont rendus accessibles que dans les cas d’utilisation prévus. De manière classique, sont mis en oeuvre des modules spécialisés responsables de l’identification, de l’autorisation des accès, de la sécurisations de communications, etc.. qui agissent de manière transverse. Comme pour la robustesse, et au-delà de ces modules spécialisés, c’est l’ensemble des modules de la solution qui doit prendre en compte l’objectif de sécurisation des accès. C’est d’autant plus important que les modules transverses ne peuvent pas gérer facilement les besoins d’accès fins et se limitent souvent à la gestion de droits d’utilisation à des groupes de fonctions. cela peut conduire à des failles de sécurité et à une inadéquation du logiciel visà-vis des exigences de sécurité (trop ou pas assez sécurisé). C’est pourquoi chaque module doit en complément mettre en oeuvre des contrôles internes sur les demandes qu’il reçoit. - le module qui connaît le métier et qui est responsable de la fourniture des fonctions peut accepter ou non une requête selon le contexte de la demande ; - comme il connaît la nature des données qu’il possède, un module peut exercer des __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 9 contrôles plus fins sur les valeurs reçues selon la fonction appelée, et par exemple, filtrer les données en fonction du rôle de l’utilisateur ; - au-delà de la protection vis-à-vis des appels utilisateur, un module peut se protéger des autres modules et vérifier leurs demandes (cela correspond à un mode de “programmation paranoïaque”, aussi connu sous le nom de “programmation par contrat”). Par exemple, si l’on donne l’ordre au module “parent d’élèves” d’enregistrer comme nombre d’enfants la valeur -1, le module doit refuser de s’exécuter ; ... Exemple : la sécurité des cartes de crédit Le modèle classique est illustré par la carte magnétique qui contient les données passives qui sont partagées par l’ensemble des terminaux de paiement utilisés. Le modèle objet est représenté par une carte à puce qui encapsule les données passives qui, ainsi, ne sont pas partagées, i.e. qui ne sont pas à la disposition du tout-venant. Les terminaux de paiement envoient des requêtes à la carte qui peut les accepter ou les refuser. Le haut niveau de sécurité globale est assuré par l’association de la sécurisation du terminal (“l’extérieur”) et de la carte à puce (“le module”). Règle #2 Le logiciel doit être composé de modules qui coopèrent entre eux, chaque module assurant le contrôle et l'intégrité de ses données. Il ne doit y avoir aucun partage de données entre modules que ce soit en mémoire, sous la forme de fichiers ou de bases de données. Chaque module doit contribuer activement à la robustesse et à la sécurité du logiciel. Pour vérifier la conformité à cette règle - La documentation du logiciel doit démontrer la qualité de la décomposition (modules intelligibles, interactions simples) - La documentation de chaque module doit décrire ses données auxquelles il accède de manière exclusive - Il est important de vérifier qu'aucune donnée passive n'est hors de contrôle d’un module. - Il est important de vérifier que les contraintes techniques sont bien propagées et traitées module par module (robustesse, sécurité, sûreté...) __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 10 Acte #3 Rattacher la solution au monde réel Afin de bien découper l’application à développer en modules, il faudrait disposer d’un moyen pour suggérer et vérifier la pertinence d’un tel découpage. Pour être réussie, l’architecture doit s’appuyer sur des images mentales maîtrisées Comprendre le logiciel, pour avoir confiance en lui et pour le faire évoluer, suppose avoir une image claire des éléments qui le composent. Pour cela, l’architecte s’appuie sur des concepts qui sont autant d’images mentales à sa disposition : - les concepts provenant du métier des utilisateurs du logiciel (ou de la vie courante), par exemple: compte-client, commande, abonnement, voyage ; - les concepts provenant de l’informatique, par exemple: fichier, journal, objet, base de données, page web... Parmi ces concepts, certains représentent des “choses” (bon de commande, permis de conduire, client, rapport d’exécution...), et d’autres, des organisations techniques du logiciel (aussi appelés patterns d’architecture). Un premier lot de modules est fait de ceux qui modélisent des éléments de la réalité L’objectif de clarté du logiciel, qui conduit à utiliser des concepts maîtrisés (issus du métier ou de l’informatique), conduit à faire en sorte que les modules soient définis en relation directe avec ces concepts, comme représentation de tout ou partie de ces concepts, et en particulier, ceux qui désignent des “choses”. Rien n’oblige, a priori, l’architecte à utiliser ces concepts comme base pour la modularité. Il pourrait découper le problème autrement, par exemple, sur la base de similitudes techniques ou de traitement qu’il identifierait. Mais alors, il prendrait un risque important, car s’il reçoit davantage de précisions à propos des besoins du client ou s’il progresse dans la compréhension du problème il peut découvrir que ce découpage devient inadapté. Quand les modules sont choisis pour modéliser chacun un élément de la réalité, les risques de mauvaises surprises sont bien moindres, la réalité servant de référence solide. On peut s’attendre à ce qu’un complément d’information sur une expression de besoin se traduise simplement en complément de développement à faire, sans impact sur le découpage. C’est particulièrement important pour les logiciels à forte complexité fonctionnelle. Une analyse descendante permet de définir des modules qui modélisent la réalité Pour aboutir à ce découpage en modules “réalistes”, le premier moyen qui est à la disposition de l’architecte est de partir de la description des besoins des utilisateurs du logiciel. Cette démarche d’analyse est dite descendante, car elle part des besoins métier (niveau haut) dans l’objectif de rallier les solutions techniques (niveau bas). Cette démarche permet d’aboutir de manière naturelle à des modules qui utilisent des concepts provenant du métier des utilisateurs du logiciel. Elle a aussi d’autres avantages comme celui de faciliter le dialogue avec le client tout au long __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 11 du projet, d’éviter de partir sur une impasse par manque de points de repère, et surtout, il rend l’architecture et le logiciel intelligibles, donc concevables, développables, vérifiables et porteurs de confiance. Règle #3 La première raison qui peut justifier l’existence d’un module logiciel est d’être une modélisation d’un élément de la réalité. Cette première catégorie de modules est issue de l’analyse descendante, c’est-à-dire en partant des besoins et en allant vers le code. Cette analyse est particulièrement importante pour les logiciels à complexité fonctionnelle forte. Pour vérifier la conformité à cette règle - Les modules créés par l'analyse descendante doivent être nommés et décrits explicitement par référence à des objets du monde réel. - Dans cette phase, aucun module ne doit avoir été créé à des fins de confort de programmation (pour le partage de code, d'autres mécanismes doivent être utilisés). - La modélisation doit être compréhensible et validée par les responsables métiers du domaine d'activité. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 12 Acte #4 Mutualiser avec des modules génériques L’analyse ascendante permet de réutiliser des composants génériques On a vu que la première piste pour créer des modules est l’approche descendante vers des modules, chacun représentant un élément de la réalité du métier. La deuxième piste et source d’images mentales maîtrisées est l’informatique elle-même. En effet, elle fournit à la fois des concepts utilisables pour la définition d’une architecture modulaire, mais également des composants logiciels qui mettent en oeuvre certains de ces concepts : il s’agit de composants logiciels disponibles dans l’entreprise, auprès des organisations Open Source ou auprès d’éditeurs de logiciels commerciaux (“COTS”, Commercial Off-The-Shelf). Le deuxième moyen qui est à la disposition de l’architecte pour aboutir à une décomposition modulaire du logiciel est donc de partir du catalogue des composants logiciels génériques, et de l’étendre par des composants nouveaux si le besoin est identifié. Cette démarche est dite ascendante, car elle part des solutions techniques (niveau bas) dans l’objectif de rallier le besoin métier (niveau haut). La conception d’un logiciel en particulier peut amener à enrichir la collection des composants génériques disponibles Le catalogue des composants génériques accessibles pour un développement est large mais il n’est pas infini. On peut donc être amené à définir de nouveaux modules génériques. Il s’agit de composants logiciels qu’on aurait bien aimé trouver dans le bestiaire des composants logiciels disponibles et dans de bonnes conditions (adéquation au problème, qualité, prix, propriété intellectuelle, contraintes d’utilisation, etc.). Quelques exemples de composants génériques : - objets graphiques à intégrer dans une page web - gestionnaire de fautes - serveur de présence - gestionnaire de traces - module de génération de rapport - module de statistiques - service de synchronisation - installateur de logiciel et de mise à jour - ... Quand on recherche un composant générique très riche, il peut correspondre à un besoin trop spécialisé, et il y a peu de chance de le trouver disponible. Quand il faut développer un composant générique, donc par nature destiné à être réutilisé, il est normal de valider sa définition par des experts techniques venant d'autres projets ou domaines. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 13 Le bon usage de composants génériques peut réduire très sensiblement le code à écrire spécifiquement pour un client Lorsqu’ils sont utilisés à bon escient, les composants génériques (à récupérer ou à créer), introduits par la démarche ascendante contribuent à limiter la quantité, la complexité et le coût du développement. Avec le développement de grandes bibliothèques de composants en interne aux entreprises et en Open Source, il est de plus en plus courant que les composants génériques représentent plus de 80% des lignes de code du logiciel. Ces composants peuvent constituer un actif majeur pour une entreprise en réduisant ses coûts projet après projet, et en la rendant plus attractive commercialement, avec des clients rassurés, et une concurrence qui aurait plus de code à créer. Règle #4 Le seconde et seule autre raison qui peut justifier l’existence d’un module logiciel est d’être un composant générique, c’est-à-dire qui a un sens général en informatique, même loin du besoin métier considéré. Cette deuxième catégorie de modules est issue de l’analyse ascendante, c’est-à-dire en partant des objets de base de l’informatique et en allant vers les besoins. Cette analyse est particulièrement importante pour les logiciels à complexité technique forte. Pour vérifier la conformité à cette règle - Le projet doit documenter la liste des composants introduits par la méthode ascendante. - Les nouveaux composants introduits par importation (en général à partir d'Open Source) ou par développement interne doivent avoir une documentation comme des produits autonomes : ce sont des actifs de l'entreprise. Une entreprise a tout intérêt à organiser un partage grâce à une forge (dépôt du logiciel et de sa documentation). - La définition des composants à partir de la conception ascendante doit être validée par des ingénieurs externes au projet afin de vérifier leur attractivité pour une réutilisation potentielle. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 14 Entracte: De l’esprit des règles et de leur application Les règles précédemment énoncées, comme toute règle de conception, ne sont pas à l’abri d’une utilisation abusive avec une interprétation littérale qui n’en respecte pas l’esprit. Le présent entracte présente deux exemples de mauvaise application des règles par interprétation trop littérale. Il finit par une discussion sur l’articulation des règles #3 et #4 qui permet d’en éclairer l’esprit. La réutilisation à mauvais escient de composants logiciels peut complexifier le logiciel et augmenter les coûts de développement Pour rendre la réalisation du logiciel moins coûteuse et moins risquée qu’un développement nouveau, on essaie souvent d’utiliser des composants génériques riches et de haut niveau3 (ex: ERP4). La réalité est plus complexe, et cette approche peut avoir un effet inverse que celui escompté. Lorsque le problème à traiter est proche des fonctions du composant générique, son adaptation et son enrichissement sont simples. Le composant générique est utilisé dans sa zone de confort, et il amène de la valeur dans le logiciel dans un cadre maîtrisé. Lorsque le problème à traiter est plus éloigné des fonctions du composant générique, son adaptation peut générer une complexité additionnelle forte. Pourquoi? parce que les concepts d’un composant de haut niveau sont de même niveau d’abstraction que les concepts de la demande du clients. S’ils sont différents, il est difficile d’utiliser les uns pour construire les autres. Dans les cas les plus extrêmes, on peut s’apercevoir au cours du développement que les deux ensembles de concepts sont contradictoires, ce qui conduit à un constat de difficulté forte, voire d’infaisabilité du développement. Exemple: calculs et affichage de graphiques Que l’on considère par exemple les exemples extrêmes : JEE5 (un composant de bas niveau) et Excel (un composant de haut niveau). - Excel est un composant générique bâti à partir du concept de feuilles de calcul. Il s’accompagne d’un langage de haut niveau (fonctions, macros) permettant d’obtenir un résultat applicatif en très peu de temps et d’efforts ; - JEE est une plate-forme logicielle associée à Java, un langage de développement générique mettant en oeuvre les concepts de programmation objet. Si le problème à traiter est proche des usages habituels des tableurs, par exemple si on souhaite faire des calculs sur des séries limitées de données tabulaires et représenter les résultats sous forme de graphique, il faudra peu de temps avec Excel: moins sans doute que pour la simple l’installation de l’environnement de développement et d’exécution Java. 3Un composant est de haut niveau quand il est proche des problèmes clients (abonné, commandes, factures,...), et qu’il est de bas niveau quand il est proche des concepts informatiques (bases de données, générateur de rapport,...) 4Enterprise Resource Planning: progiciel de gestion intégré comme, par exemple, SAP ERP 5 __________ Java Enterprise Edition: environnement logiciel Java pour faire des applications Web Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 15 Utiliser Excel est justifié car il est dans sa zone de confort. Mais voilà, quand le travail se complique (on a besoin de processus métier et pas seulement de fonctions, on doit gérer plusieurs utilisateurs...), le développeur Java peut absorber les nouvelles demandes, tandis que le développeur sous Excel voit la complexité de sa tâche exploser: profusion de fonctions, de macros, des tables intermédiaires de plus en plus nombreuses, au final tout un arsenal d’astuces pour contourner les limitations, et un logiciel qui devient non maîtrisable. En règle générale, plus un logiciel porte une valeur ajoutée grande (richesse et haut niveau), plus il a été fait d’hypothèses sur son environnement et plus son domaine d’application est réduit, et plus vite s’écroule la productivité quand on s’écarte du domaine. Une analyse montre que c’est la combinaison ad-hoc de fonctions de natures différentes qui restreint le domaine d’application. On peut noter que les grands succès de l’Open Source sont des logiciels mono-fonction (“loi de Boileau”). Malheureusement, il n’est pas rare que la peur de lancer un développement à partir d’une feuille blanche peut conduire à utiliser un logiciel existant quitte à le tordre loin de son domaine d’application, et obtenir au final plus de développements à faire, un coût énorme et une maintenabilité hasardeuse. La volonté d’organiser l’architecture ne doit pas conduire à rendre le logiciel incompréhensible L’esprit des règles d’architecture 3 et 4 est qu’il faut donner un sens aux modules créés, pour cette modularité aide à maîtriser le logiciel. Ces règles s’opposent donc à la création d’objets pour la simple commodité de programmation. Cela peut générer un conflit avec le principe de programmation dit “DRY” (Do not Repeat Yourself). L’interprétation littérale de ce principe est qu’il faut absolument éviter de recoder les lignes de code identiques ou similaires, et pour cela il faut les placer dans un module unique et appeler ce module plusieurs fois. Si l’on peut éviter une redondance de code à travers la création d’un objet générique (règle #4), c’est bien. Mais il ne faut pas créer des objets incompréhensibles (ni signification métier, ni signification générique) uniquement pour éviter une redondance. Dans ce cas d’espèce, l’esprit de la règle #4 s’impose sur la lettre du principe DRY. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 16 Un peu de philosophie sur l’informatique et les sciences Le mode opératoire des sciences physiques est assez connu de nos jours: le physicien jette des ponts entre la réalité et les concepts mathématiques. Pour cela, le physicien définit un modèle de la réalité en utilisant des objets mathématiques et des relations entre ces objets. Ce modèle permet de calculer sur les objets mathématiques et d’obtenir des résultats, pour le compte du monde réel. Par exemple, le physicien dit que la table est modélisée par un rectangle. Le mathématicien a fabriqué l’objet de haut niveau “rectangle” à partir de ses objets géométriques de base et a obtenu la formule “longueur x largeur” pour calculer sa surface. Le physicien utilise alors cette formule pour la table. Par analogie, on pourrait dire que la démarche descendante de la règle #3 qui crée des modules associés au réel est une démarche de type “sciences physiques”. Et de même, la démarche ascendante de la règle #4 qui élabore des composants génériques à partir d’autres composants informatiques de base, est une démarche de type “mathématiques”. Dans une équipe d’architectes logiciels, certains travaillent plutôt sur la modélisation du réel, comme les physiciens, d’autres sur l’élaboration ou la combinaison d’objets informatiques, comme les mathématiciens. C’est de cette démarche bi-directionnelle que naissent les bonnes architectures. Il est bon de garder un équilibre entre les approches descendantes et ascendantes On peut remarquer que, selon le type de logiciel, l’approche descendante ou l’approche ascendante peut être dominante, et en particulier: - les logiciels soumis à de fortes demandes fonctionnelles (ex: nombreux écrans), comme les logiciels de gestion, font plus appel à la démarche descendante pour bien prendre en compte ces demandes. - les logiciels soumis à de fortes contraintes techniques (ex: tolérance aux pannes), comme les logiciels de réseaux, font plus appel à la démarche ascendante pour relever les défis techniques. Il est cependant nécessaire d’éviter une seule démarche, car une trop grande exclusive d’approche descendante nuit à la réutilisation des développements, et une trop grande approche ascendante nuit à la prise en compte des demandes clients. Comme la plupart des architectes privilégient l’un ou l’autre de ces modes, la présence et la discussion entre ces deux courants de pensée dans l’équipe de conception est un facteur clé de succès. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 17 Acte #5 Rechercher l'indépendance et les degrés de liberté Un peu de rangement des modules peut être utile A ce stade, la compréhension du travail d’architecture a progressé: - le tri doit être fait entre vraies et fausses contraintes ; - le logiciel doit être une collection de modules qui interagissent sans partager de données ; - des modules modélisent des éléments du réel... - ...tandis que les autres réalisent des fonctions informatiques génériques. Le nombre d’éléments manipulés (modules, sous-modules, composants logiciels) est important. Le maillage entre ces éléments peut être complexe. L’objectif maintenant considéré est de faire en sorte que l’architecture soit simple, compréhensible et puisse réduire l’ampleur du développement. Rappel: comme expliqué dans le prologue, l’introduction des règles est faite à travers un parcours dans le but de faciliter la présentation, sans que cela n’implique un caractère séquentiel dans les travaux opérationnels. En particulier, l’objectif d’orthogonalité est pris en compte pendant le découpage en modules et pas a posteriori. Le concept d’orthogonalité aide à structurer les logiciels Si l’on considère deux axes perpendiculaires, on dit aussi orthogonaux, le déplacement d’un point parallèlement à un axe n’a pas d’impact sur sa projection le long de l’autre axe. Le concept d'orthogonalité logicielle est ainsi dérivé de la géométrie et de l’idée d’indépendance entre deux choix (ou “positions”), chacun parmi une liste (ou “axe”). Pour bien comprendre cette notion, considérons, par exemple, le cas d’un ordinateur individuel. On installe sur cet ordinateur une collection de logiciels d’application: traitement de texte, tableur, éditeur de présentations, navigateur web, etc. Comme il faut parfois imprimer, on s’assure que l’on peut soumettre des impressions à une ou plusieurs imprimantes. Mais, pour qu’un ordinateur puisse envoyer un travail à une imprimante, il lui faut parler son “langage”. Pour que ce soit le cas, on installe sur l’ordinateur un petit logiciel appelé “pilote” (en anglais, “driver”) et ce, pour chaque type d’imprimante. Il y a donc sur l’ordinateur 2 groupes de logiciels - les applications - les pilotes. On remarque que l’on peut utiliser n’importe quelle application avec n’importe quel pilote. On peut aussi ajouter ou retrancher une application ou un pilote sans impact sur le reste. Une façon géométrique à 2 dimensions de représenter ces logiciels est de tracer deux axes orthogonaux: l’axe des applications et l’axe des pilotes. Sur chaque axe on place les logiciels correspondants. A partir de ces deux axes perpendiculaires, “embrochant” l’un les applications, l’autre les pilotes, on peut retrouver sur le plan défini par les axes, tous les cas d’usage fonctionnel avec __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 18 une application A soumettant une impression à une imprimante P. On dit que l’on a une orthogonalité à deux dimensions entre les applications et les pilotes des imprimantes. L’avantage est clair par rapport à une situation où il faudrait faire des logiciels intégrant à la fois du code application (ex: un traitement de texte) et le support de plusieurs imprimantes. C’était le cas autrefois avec MS-DOS. Grâce à l’orthogonalité entre applications et pilotes, si l’on a 30 applications et 10 pilotes d’imprimantes, on supporte 30x10=300 cas d’usage, alors qu’il n’aura fallut développer que 30+10=40 logiciels. On retrouve le même avantage pour la mise au point et les tests. Imaginons que l’on ait à développer cet ensemble fonctionnel: une équipe “application” peut utiliser un pilote factice (qui affiche simplement ce qu’il reçoit) tandis qu’une équipe “pilote” utilisera une application factice (qui ne fait qu’envoyer quelque chose de connu vers l’imprimante). Par la suite, on peut garder des logiciels factices à côté des vrais pour faciliter la maintenance. L’idée générale, est que l’architecture doit mettre le maximum de modules sur des axes, et sur le maximum d’axes. On dit alors que l’on a maximisé l’orthogonalité. Certains axes peuvent ne porter qu’un seul logiciel. Cet axe apporte l’indépendance du logiciel par rapport aux autres axes: c’est le premier avantage de l’orthogonalité. Les axes qui portent plusieurs logiciels apportent, en plus, une flexibilité et extensibilité à bon compte: ajouter un logiciel sur un axe n’a pas d’impact sur le reste: c’est le deuxième avantage de l’orthogonalité. Quelques exemples de dimensions: - l’axe sur lequel on place les fonctions de calcul du rabais consenti au client (selon la catégorie du client, le rabais est un pourcentage fixe, ou augmente avec le volume de la commande, etc.) - l’axe sur lequel on place les fonctions qui font la saisie d’un formulaire de permis de conduire (selon la catégorie du véhicule il faut des informations différentes) - l’axe sur lequel on place les fonctions de décodage de vidéos (mpeg2, mpeg4, wmv, etc.) - etc. Pour chaque axe, on a une collection de logiciels et on utilise un indicateur pour aiguiller le traitement vers celui qui correspond au besoin. L’orthogonalité est un puissant moyen de lutte contre la complexité des logiciels Augmenter l’orthogonalité est le moyen architectural le plus efficace pour lutter contre la complexité, et rendre ainsi humainement maîtrisables les logiciels les plus imposants. Il s'agit d’un concept d'architecture assez abstrait, mais son efficacité justifie des efforts. Un code orthogonal favorise la maintenance et l'évolution: - Un nouveau logiciel ajouté sur un axe va pouvoir fonctionner avec les autres de la même manière que ses pairs de l’axe (ex: l’ajout d’un pilote d’imprimante n’oblige pas à changer le traitement de texte): les temps et les coûts de développement en sont réduits. - La qualité est améliorée car il est plus facile de tester chaque partie du code, indépendamment des autres, et pas simplement le code global. - Il est plus facile pour répartir le travail entre de petites équipes indépendantes, qui peuvent être plus motivées puisque chaque module est indépendant: le Lego plutôt que le puzzle. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 19 - Le code total à écrire est moins volumineux comme on l’a vu avec l’exemple des applications et des pilotes d’imprimante. On a remplacé des multiplications par des additions. - Lors de la phase de développement, l'orthogonalité facilite la mise en place progressive des modules. Au début du développement, on peut valider le squelette de l'application en mettant en place des modules quasi-vides et réaliser un test global de fonctionnement (le "Hello Word !" de la solution). Puis, on code les modules véritables au côté des “factices”. Pour la même raison, l’orthogonalité facilite l’introduction de nouvelles versions d’un module en commençant par les placer à côté des anciennes. Dans chaque environnement technique, il y a des moyens pour mettre en oeuvre pratiquement l’orthogonalité La mise en pratique du concept architectural d’orthogonalité dans le code dépend de la technologie générale du logiciel. - en langage C, un axe est matérialisé par un tableau de pointeurs de fonctions, chaque pointeur correspondant à un module de la dimension. On retrouve cela avec Unix. - quand les modules d’un axe sont appelés par des Web Services, chacun est identifié par son adresse Web, et donc une dimension est matérialisée par une table d’adresses web (url). - en Java, une dimension est souvent caractérisée par une “interface” qui définit les fonctions attendues. Chaque module est une classe qui doit implémenter cette interface pour être sur l’axe. - ... Un peu d’histoire de l’orthogonalité Le premier usage du terme “orthogonalité” a été fait à propos des compilateurs. Un compilateur est un programme qui traduit automatiquement le “code source” d’un logiciel écrit dans un langage compréhensible par les humains (ex: langage C), en un “code objet” écrit dans le langage compréhensible par le processeur (ex: Intel 386), afin que celui-ci puisse l’exécuter. Le langage des premiers microprocesseurs était très irrégulier. Deux des registres6 du processeur étaient les seuls utilisables pour les multiplications. Un autre registre servait à faire une lecture dite “indexée” dans la mémoire de l’ordinateur, etc. Une bonne traduction automatique est très difficile avec les processeurs irréguliers. Elle est beaucoup plus efficace avec des processeurs plus récents et orthogonaux: toutes les instructions de calcul peuvent porter sur toutes les données (n’importe quel registre, mémoire directement désignée, etc.) Un peu d’histoire encore. Quand on traite d’orthogonalité, on doit aussi citer Unix (1970) comme un exemple particulièrement réussi. Ses concepts ont toujours du succès plus de 40 ans après sa 6un registre est une mémoire interne au processeur __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 20 création, à travers différentes mises en oeuvre: iOS de iPhone/iPad, Android, Mac OS X ou Linux. Règle #5 L’architecture doit placer le plus possible de modules, métiers ou techniques, sur des axes orthogonaux. Un module placé sur un axe n'est connu des autres qu’en tant que membre de cet axe. Si plusieurs modules sont sur le même axe, les autres modules les utiliseront de manière unifiée, et le logiciel global n’en sera que simplifié. Pour vérifier la conformité à cette règle - L’appartenance ou non-appartenance à des axes pour un module doit être vérifiée avec le critère d’indépendance. - Les architectes doivent vérifier si on ne peut pas créer des axes sur lesquels on peut placer le plus grand nombre possible de modules __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 21 Acte #6 Faciliter les autres activités de conception (codage, tests, etc.) Les règles vues précédemment conduisent à une architecture qui fait du logiciel un ensemble de modules bien choisis et communiquants. Cependant, l’architecture ne doit pas tenir compte uniquement des services que doit rendre la solution: elle doit aussi faciliter le codage et les tests du logiciel. Les constructeurs automobiles savent qu'une partie importante de l'effort d’architecture d'un nouveau véhicule à pour objet de faciliter sa construction et d’en réduire le coût. En logiciel, il n’y a pas de coût de production, celle-ci étant automatisée (assurée par le compilateur et l’éditeur de liens), mais il y a un coût de conception important: l’écriture des lignes de codes, les tests et les corrections. Le travail à plusieurs personnes ou équipes, est le défi permanent du développement logiciel, qui, comme la plupart des activités de conception, s’y prête a priori mal - comment sera-t-il possible de partager le travail entre les équipes de façon à ce qu’il ne soit pas nécessaire pour chacune de maîtriser l’ensemble? ex: en définissant plusieurs modules, chaque module étant responsable d’un ensemble de données métier et d’actions possibles sur elles → le défi est la rigueur dans l'intégrité des données, et en définissant des modules orchestrant la progression des processus en invoquant les premiers → le défi est l'interaction globale ; - comment sera-t-il possible de tirer profit de la diversité des talents humains? ex: en séparant les travaux sur le contenu des écrans de ceux sur l’apparence visuelle, ce qui est facilité si ces travaux correspondent à des modules différents. ex: en tirant profit des goûts et facilités des développeurs pour les attributions des modules. Certains modules sont proches des métiers et des utilisateurs avec une difficulté qui tient à la gestion de la richesse fonctionnelle, et d’autres modules sont plus internes et qui portent des difficultés techniques précises L’architecture doit faciliter le travail de codage et de mise au point - comment va être codé le logiciel: technologies, outils individuels et collectifs ex: choisir, par exemple, une architecture basée sur Android pour un équipement, fait bénéficier l’équipe de codage d’une large base d’outils et d’environnements matériels pour les tests. - comment seront gérés les incidents de traitement? ex: quand un incident se produit sur un module au cours d’un traitement global, la reprise du cours des opérations peut être difficile avec des risques d’incohérence. Si l’architecture prévoit que les requêtes entre modules soient idempotentes7, les modules peuvent simplement relancer leurs requêtes après un incident, évitant des mécanismes de resynchronisation délicats. 7On dit qu’une fonction est idempotente quand le fait de l’appliquer plusieurs fois est équivalent à l’appliquer une seule. Par exemple, “effacer l’article 23561” est idempotent, alors que “ajouter 1 au nombre de visites” ne l’est pas. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 22 - comment sera testé chaque module? ex: si les modules communiquent entre eux par des messages simples et lisibles par des humains cela facilite la mise au point. Si le mécanisme d’envoi est de type mail ou web, c’est une facilité supplémentaire. L’architecture prépare l’assemblage des modules en un logiciel global - comment les objets communiqueront entre eux? ex: la communication entre objets par REST est testable avec un navigateur, la communication entre objets par mail est testable avec un client de messagerie - comment sera testée la solution lors son installation éventuelle dans un site éloigné? ex: des auto-tests intégrés dans le logiciel lui-même permettent aux équipes de déploiement de les activer sur site à des fins de contrôle - comment sera assuré le suivi des anomalies détectées par le logiciel lui-même? ex: la mise en oeuvre d’une centralisation des tickets d’anomalies venant des divers modules et le transfert vers un site web d’aide à la maintenance Règle #6 L’architecture d’un logiciel doit faire des choix sur le découpage en modules et sur leurs interactions afin de faciliter les autres activités de conception: codage, test, maintenance. En particulier, le découpage doit permettre à des équipes différentes de travailler en parallèle et sans que chacune ait à maîtriser l’ensemble. Le mode d’interaction entre modules doit faciliter les tests et la maintenance. Pour vérifier la conformité à cette règle - Le document d'architecture doit couper la solution en éléments à réaliser séparément: les équipes de développement doivent s’en assurer - Pour s’assurer que l’architecture prend bien en compte le codage qui va suivre, il est bien qu’il donne la voie à suivre (environnement de développement, de test et de débogage, approche à suivre pour la mise au point). __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 23 Épilogue Les 6 règles d’architecture Règle #1 L’architecture doit exclure les fausses contraintes, souvent issues d’une traduction trop rapide des besoins en choix techniques, car elles peuvent être à l’origine de dangereuses décisions d’architecture. Règle #2 Le logiciel doit être composé de modules qui coopèrent entre eux, chaque module assurant le contrôle et l'intégrité de ses données. Il ne doit y avoir aucun partage de données entre modules que ce soit en mémoire, sous la forme de fichiers ou de bases de données. Chaque module doit contribuer activement à la robustesse et à la sécurité du logiciel. Règle #3 La première raison qui peut justifier l’existence d’un module logiciel est d’être une modélisation d’un élément de la réalité. Cette première catégorie de modules est issue de l’analyse descendante, c’est-à-dire en partant des besoins et en allant vers le code. Cette analyse est particulièrement importante pour les logiciels à complexité fonctionnelle forte. Règle #4 Le seconde et seule autre raison qui peut justifier l’existence d’un module logiciel est d’être un composant générique, c’est-à-dire qui a un sens général en informatique, même loin du besoin métier considéré. Cette deuxième catégorie de modules est issue de l’analyse ascendante, c’est-à-dire en partant des objets de base de l’informatique et en allant vers les besoins. Cette analyse est particulièrement importante pour les logiciels à complexité technique forte. Règle #5 L’architecture doit placer le plus possible de modules, métiers ou techniques, sur des axes orthogonaux. Un module placé sur un axe n'est connu des autres qu’en tant que membre de cet axe. Si plusieurs modules sont sur le même axe, les autres modules les utiliseront de manière unifiée, et le logiciel global n’en sera que simplifié. Règle #6 L’architecture d’un logiciel doit faire des choix sur le découpage en modules et sur leurs interactions afin de faciliter les autres activités de conception: codage, test, maintenance. En particulier, le découpage doit permettre à des équipes différentes de travailler en parallèle et sans que chacune ait à maîtriser l’ensemble. Le mode d’interaction entre modules doit faciliter les tests et la maintenance. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 24 Les explications des chapitres précédents auront peut être montré que ces règles sont bien comme voulu: ○ Simples à comprendre et à partager, et en particulier, elles sont compréhensibles sans avoir une expertise technologique particulière ; ○ Minimales, en ce sens qu’elles sont peu nombreuses, qu’elles s’appliquent pour la grande majorité des projets de développement logiciel, et que le retrait de l’une d’elles ouvrirait la porte à un danger sérieux ; ○ Efficaces, car si une architecture est conforme à ces règles, alors il y a de grandes chances pour que le développement se fasse dans de bonnes conditions. Les règles d’architecture sont indépendantes des méthodologies À partir des années 70, des méthodologies ont été inventées pour maîtriser la complexité des projets logiciels depuis le classique "cycle en V" jusqu'aux méthodes agiles. L'approche présentée ici avec ces règles est complémentaire des méthodologies, car il s'agit là non pas de maîtriser la complexité, mais de la réduire. Aucune méthodologie spécifique n'est encouragée ici, même si ces règles peuvent faciliter les approches incrémentales. Les règles d’architecture sont indépendantes du domaine d’application Elles sont génériques et applicables pour la conception de solutions logicielles de tous types. Cela inclut, par exemple, aussi bien les systèmes d'information que les logiciels “temps réels” des équipements embarqués. La politique technique que se choisit une organisation doit aller bien sûr au-delà de ces règles avec des choix de technologies, de logiciels, d’outils de développement, de pratiques, etc. Ces choix, eux, doivent être faits selon le type de problèmes à traiter. Ces règles ont pour objet la sécurisation des développements logiciels grâce à une architecture saine, plaçant la qualité en objectif premier. Elles n’entraînent en elles-mêmes ni modernité, ni conformité par rapport à telle ou telle orientation technique. Les règles d’architecture sont indépendantes des générations de l’informatique Pour mémoire... Les années 1980 ont vu la “Génération Unix” prendre le dessus sur les mainframes - une solution est alors une application qui tourne sur un ordinateur (départemental ou personnel); elle traite chaque touche appuyée par l’utilisateur et l’écran - les technologies sont Unix, le PC MS-DOS, le langage C, les terminaux asynchrones - Digital Equipment (DEC) est le leader; les standards Unix et DOS ont dé-verticalisé l’industrie avec des éditeurs de logiciel, des fabricants de matériel et des sociétés de services. Les années 1990 ont vu la “Génération Informatique Distribuée” tirer parti des réseaux - les solutions informatiques sont pour la première fois réparties entre plusieurs applicatifs __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 25 tournant sur des ordinateurs en réseau - les technologies mettent en oeuvre le modèle client-serveur avec souvent le poste de travail Windows comme client, le partage des fichiers, les serveurs de bases de données - Microsoft côté ordinateurs personnels et Sun côté stations et serveurs sont les leaders; l’industrie découvre la production de masse des ordinateurs; l’éclatement des solutions sur plusieurs machines en réseau favorise la segmentation du marché des logiciels (bases de données, ERP, middleware transactionnel, etc.) Les années 2000 voit la “Génération Java” dominer - les solutions sont basées sur des applications Web et les utilisateurs y accèdent par un navigateur à travers un réseau, l’accès à Internet étant devenu banal - les technologies sont Java, JEE, HTML, l’architecture Web avec le protocole http - Oracle (BEA, Sun Javasoft) et IBM (WebSphere) sont les leaders de ce monde Java/Web; l’économie du Web s’envole avec l’e-commerce, le multimédia en ligne (légal ou pas), les nombreux sites d’information, les modèles économiques basés sur la publicité, etc. Les années 2010 et la “Génération Google” prennent le leadership - des solutions centrées sur l’utilisateur individu ou la communauté; des smartphones et des tablettes; des communautés Facebook ou autres - les technologies iPhone/iPad (Apple) et Android (Google); Web2.0 et Google Web Toolkit; (Google); bases NoSQL comme BigTable (Google) et Cassandra (Facebook); les échanges entre les applications de Facebook et Google et ceux de leurs partenaires; l’architecture Web généralisée, les clouds d’Amazon, Google et Apple - Google, Facebook et Apple sont les leaders technologiques et les leaders en notoriété, les “decisions makers” ont connu l’informatique dans leur jeunesse et sont exigeants; l’Open Source devient le lieu de l’innovation Comme on le voit, les règles d’architecture ne se rapportent ni à une génération particulière, ni à une technologie spécifique, même si les dernières générations fournissent des technologies favorables. __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 26 Un peu d’histoire, avec les principes d’architecture de l’Internet La clairvoyance de Robert Kahn, co-inventeur de l'architecture Internet avec Vinton Cerf, l’a conduit à faire quatre choix fondateurs: “- Chaque réseau distinct devrait être autonome et aucun changement interne ne devrait être nécessaire au niveau de ce réseau pour pouvoir le connecter à Internet. - Les communications devraient s'effectuer selon un service sans garantie. Si un paquet de données ne parvenait pas à la destination finale, il devrait être retransmis à partir de la source dans un court délai. - Des boîtes noires devraient être utilisées pour connecter les réseaux [On les appellera plus tard des passerelles et des routeurs]. Aucune information ne devrait être conservée par les passerelles concernant les flux de paquets individuels qui les traversent, favorisant ainsi leur simplicité et évitant une adaptation et une récupération complexes à partir de divers modes d'échec. - Il ne devrait y avoir aucun contrôle global au niveau opération.” Il est intéressant de noter que Internet, pourtant loin du sujet “architecture du logiciel”, respecte les 6 règles. On plaçait beaucoup de contraintes sur les anciens réseaux: pas de pertes de données, séquencement respecté, facturation, etc. Au final, Internet l’a emporté, et il exclut ces fausses contraintes en traitant le seul service attendu d’un réseau: vaincre la distance. (Ceci étant acquis, il reste du travail sur les autres services comme voix, web, chat, commerce, etc.) → règle #1 La modularité est exemplaire avec des fonctions simples et segmentées (du LAN au routeur) → règle #2 La modélisation est faite à la fois sur les services vus par les utilisateurs (paquet ou flux) → règle #3, et sur les bases informatiques (réseaux physiques) → règle #4 Internet créé une orthogonalité entre les applications connectées et les réseaux physiques. Ce n’était pas le cas avant, dans les télécommunications, où il y avait une intégration verticale → règle #5 Les services et protocoles sont conçus pour faciliter la mise au point avec, par exemple, des protocoles simples à visualiser, voire en clair → règle #6 __________ Les 6 règles d’architecture pour sécuriser le développement logiciel par Denis Attal, Raymond Wei 27