Résumé de L
Résumé de L
Introduction
Une méthodologie est un ensemble de lignes directrices qui vous aident à atteindre votre objectif de
manière efficace, ce qui vous permet de gagner du temps, de mettre en œuvre des solutions à long
terme et d'éviter de réinventer la roue à chaque fois.
L’architecture est propre, c'est-à-dire qu'il est facile de comprendre ce qui se passe. L'architecture
propre est à l'opposé du code spaghetti, où tout est entrelacé et où aucun élément ne peut être
facilement détaché du reste et remplacé sans que l'ensemble ne soit endommagé et pousse
l'abstraction à ses limites.
Architecture logicielle détaille la manière dont un processus est mis en œuvre à une certaine
granularité, compte tenu de certaines hypothèses ou exigences. La qualité d'une architecture peut
alors être jugée sur la base de paramètres tels que son coût, la qualité des résultats, sa simplicité ou
son "élégance", la quantité d'efforts nécessaires pour la modifier, etc.
Pourquoi architecturer :
Le cadre web : C’est lorsqu’un utilisateur utilise un URL pour faire des recherches à travers des
chambres à louées. Le but du cadre web est de comprendre la requête HTTP et de récupérer les
données dont nous avons besoin pour fournir une réponse. Dans ce cas simple, il y a deux parties
importantes dans la requête, à savoir le point d'accès lui-même (/rooms) et un seul paramètre de la
chaîne de requête, status=available. Les points d'accès sont comme des commandes pour notre
système. Lorsqu'un utilisateur accède à l'un d'entre eux, il signale au système qu'un service
spécifique a été demandé, qui, dans ce cas, est la liste de toutes les chambres disponibles à la
location.
Logique d'entreprise : La logique d'entreprise est l'algorithme ou le processus spécifique que vous
souhaitez mettre en œuvre, la manière dont vous transformez les données pour fournir un service.
C'est la partie la plus importante du système.
Il est toujours dangereux de ne pas savoir clairement ce qu'un composant peut ou doit faire, car les
domaines d'influence et de contrôle se chevauchent naturellement. Cela peut entraîner toutes sortes
de problèmes, allant de simples inefficacités à des blocages complets.
Types de données
Les types de données, c'est-à-dire la manière dont nous encapsulons et transmettons les
informations, jouent un rôle important dans un système. En particulier, lorsque nous parlons de
systèmes logiciels, nous devons nous assurer que les types partagés par différents systèmes sont
connus de tous.
Ainsi, lorsque nous considérons un système logiciel, nous devons comprendre quelle partie définit les
types et le format des données (le "langage") et veiller à ce que les dépendances qui en résultent
n'entravent pas le travail de l'exécutant.
# Entités : La couche entité contient une représentation des modèles du domaine, c'est-à-dire tout ce
qu’un système doit interagir et qui est suffisamment complexe pour nécessiter une représentation
spécifique.
Cette couche contiendra probablement des classes, avec des méthodes qui simplifient l'interaction
avec elles. Il est cependant très important de comprendre que les modèles de cette couche sont
différents des modèles habituels des frameworks comme Django. Ces modèles ne sont pas connectés
à un système de stockage, ils ne peuvent donc pas être directement sauvegardés ou interrogés en
utilisant leurs propres méthodes, ils ne contiennent pas de méthodes pour se décharger en chaînes
JSON, ils ne sont pas connectés à une couche de présentation. Ce sont des modèles dits légers.
#Cas d'utilisation :
Les cas d'utilisation constituent la partie la plus importante d'un système propre, car ils mettent en
œuvre les règles commerciales, qui sont la raison d'être du système lui-même.
Les cas d'utilisation ont un accès total à la couche des entités, ils peuvent donc les instancier et les
utiliser directement. Ils peuvent également s'appeler les uns les autres, et il est courant de créer des
cas d'utilisation complexes en composant des cas d'utilisation simples.
#Passerelles
Cette couche contient des composants qui définissent des interfaces pour des systèmes externes,
c'est-à-dire un modèle d'accès commun à des services qui ne mettent pas en œuvre les règles de
l'entreprise. La couche des passerelles est intimement liée à la couche des systèmes externes.
#Systèmes externes
Cette partie de l'architecture est constituée de composants qui mettent en œuvre les interfaces
définies dans la couche précédente.
Plus une couche est profonde dans cette architecture, plus le contenu est abstrait.
La règle d’or : parler vers l'intérieur avec des structures simples, parler vers l'extérieur avec des
interfaces.
Transmission des données à des éléments plus abstraits, en utilisant des structures de base, c'est-à-
dire des entités et tout ce qui est fourni par le langage de programmation que vous utilisez
• Un identifiant unique
• Latitude et longitude
Sérialiseurs
Les couches externes peuvent utiliser le modèle Room, mais si vous souhaitez renvoyer le modèle à
la suite d'un appel à l'API, vous avez besoin d'un sérialiseur. Le format de sérialisation typique est
JSON, car il s'agit d'une norme largement acceptée pour les API basées sur le web. Le sérialiseur ne
fait pas partie du modèle mais est une classe spécialisée externe qui reçoit l'instance de modèle et
produit une représentation de sa structure et de ses valeurs.
Cas d'utilisation
Il est temps de mettre en œuvre la logique commerciale qui s'exécute à l'intérieur de notre
application. Les cas d'utilisation sont les endroits où cela se produit, et ils peuvent ou non être
directement liés à l'API externe du système
Le système de stockage
Installation du flacon :
Les fixtures peuvent être définies directement dans votre fichier de test, mais si nous voulons qu'une
fixture soit globalement disponible, le meilleur endroit pour la définir est le fichier [Link] qui est
automatiquement chargé par pytest. Comme vous pouvez le voir, il y a beaucoup d'automatisation,
et si vous n'en êtes pas conscient, vous pourriez être surpris par les résultats, ou frustré par les
erreurs.
.point de terminaison
Les points de terminaison sont en fait des fonctions qui sont exécutées lorsqu'un utilisateur envoie
une requête à une certaine URL, de sorte que nous pouvons toujours travailler avec TDD, puisque
l'objectif final est d'avoir un code qui produit certains résultats.
WSGI
Les applications web Python exposent une interface commune appelée Web Server Gateway
Interface²⁸ ou WSGI. Ainsi, pour exécuter le serveur web de développement Flask , nous devons
définir un fichier [Link] dans le dossier principal du projet, c'est-à-dire dans le même répertoire que
le fichier
Demandes et réponses
Nous pouvons diviser le code de gestion des erreurs en deux zones différentes. Le premier
représente et gère les demandes, c'est-à-dire les données d'entrée qui parviennent à notre cas
d'utilisation. La seconde couvre la manière dont nous renvoyons les résultats du cas d'utilisation par
le biais des réponses, les données de sortie. Ces deux concepts ne doivent pas être confondus avec
les requêtes et les réponses HTTP, même s'ils présentent des similitudes.
Structure de base
Nous pouvons mettre en œuvre des requêtes structurées avant d'étendre le cas d'utilisation à
l'acceptation de filtres. Nous avons juste besoin d'une classe RoomListRequest qui peut être
initialisée sans paramètres, alors créons le fichier tests/requests/test_room_list.py et mettons-y un
test pour cet objet.
Demande de validation
Le paramètre filters que nous voulons ajouter au cas d'utilisation permet à l'appelant d'ajouter des
conditions pour restreindre les résultats de l'opération de liste de modèles, en utilisant une notation
du type . Par exemple, en spécifiant filters={'price lt' : 100}, tous les résultats dont le prix est inférieur
à 100 seront renvoyés.
Réponses et échecs
Il existe un large éventail d'erreurs qui peuvent se produire lors de l'exécution du code du cas
d'utilisation. Des erreurs de validation, comme nous venons de le voir dans la section précédente,
mais aussi des erreurs de logique d'entreprise ou des erreurs provenant de la couche de référentiel
ou d'autres systèmes externes avec lesquels le cas d'utilisation s'interface.
Notre implémentation des requêtes et des réponses est enfin terminée, nous pouvons donc
maintenant implémenter la dernière version de notre cas d'utilisation. La fonction
room_list_use_case ne dispose toujours pas d'une validation correcte de la requête entrante, et ne
renvoie pas de réponse appropriée au cas où quelque chose aurait mal tourné.
Les systèmes externes qui dépendent d'une certaine version de l'API ne fonctionnent qu'après un
test d’intégration
Le serveur HTTP
Le dépôt
L'interface de programmation
À ce stade, il est extrêmement simple de corriger le CLI, car il suffit d'imiter ce que nous avons fait
pour le serveur HTTP, mais sans tenir compte des filtres, car ils ne font pas partie de l'outil de ligne
de commande.
L'architecture propre que nous avons conçue dans les chapitres précédents définit un cas
d'utilisation qui reçoit une instance de référentiel comme argument et utilise sa méthode de liste
pour récupérer les entrées contenues. Cela permet au cas d'utilisation de former un couplage très
lâche avec le référentiel, n'étant connecté qu'à travers l'API exposée par l'objet et non à
l'implémentation réelle. En d'autres termes, les cas d'utilisation sont polymorphes en ce qui concerne
la liste des méthodes. C'est très important et c'est le cœur de la conception d'une architecture
propre. Étant reliés par une API, le cas d'utilisation et le référentiel peuvent être remplacés par
différentes implémentations à tout moment, à condition que la nouvelle implémentation fournisse
l'interface demandée. Il convient de noter, par exemple, que l'initialisation de l'objet ne fait pas
partie de l'API utilisée par les cas d'utilisation puisque le référentiel est initialisé dans le script
principal et non dans chaque cas d'utilisation. La méthode init n'a donc pas besoin d'être la même
pour toutes les implémentations du référentiel, ce qui nous donne une grande flexibilité, car
différents systèmes de stockage peuvent avoir besoin de différentes valeurs d'initialisation. Le
référentiel simple que nous avons mis en œuvre dans l'un des chapitres précédents est le suivant
dont l'interface est composée de deux parties : l'initialisation et la liste des méthodes. La méthode
init_- _ accepte des valeurs car cet objet spécifique ne joue pas le rôle de stockage à long terme, et
nous sommes donc obligés de transmettre des données à chaque fois que nous instancions la classe.
Un référentiel basé sur une base de données appropriée n'aura pas besoin d'être rempli de données
lors de son initialisation, sa tâche principale étant de stocker des données entre les sessions, mais il
devra néanmoins être initialisé au moins avec l'adresse de la base de données et les identifiants
d'accès.
On peut y accéder depuis Python de plusieurs façons, mais la meilleure est probablement l'interface
SQLAlchemy³². SQLAlchemy est un ORM, un paquetage qui fait correspondre des objets (comme
dans l'orientation objet) à une base de données relationnelle. Les ORM se trouvent généralement
dans des frameworks web comme Django ou dans des packages autonomes comme celui que nous
envisageons.
Ce qui est important à propos des ORM, c'est qu'ils sont de très bons exemples de ce qu'il ne faut pas
essayer de simuler. Le fait de simuler correctement les structures SQLAlchemy qui sont utilisées lors
de l'interrogation de la base de données résulte en un code très complexe qui est difficile à écrire et
presque impossible à maintenir, car chaque changement dans les requêtes résulte en une série de
simulations qui doivent être réécrites³³.
La première chose à faire est d'étiqueter les tests d'intégration, de les exclure par défaut et de créer
un moyen de les exécuter. Puisque pytest supporte les étiquettes, appelées marques, nous pouvons
utiliser cette fonctionnalité pour ajouter une marque globale à un module entier. L'attribut de
module pytestmark étiquette chaque test du module avec la balise integration. Pour vérifier que cela
fonctionne, j'ai ajouté une fonction de test test_dummy qui réussit toujours. Le marqueur doit être
enregistré dans [Link]
La création et l'alimentation de la base de données de test avec les données initiales feront partie de
la suite de tests, mais nous devons définir quelque part les tables qui seront contenues dans la base
de données. C'est ici que l'ORM de SQLAlchemy entre en jeu, car nous définirons ces tables en
termes d'objets Python
Gestion de l'orchestration
Lorsque nous exécutons les tests d'intégration, le moteur de base de données Postgres doit déjà
fonctionner en arrière-plan et être configuré, par exemple, avec une base de données vierge prête à
être utilisée. De plus, lorsque tous les tests ont été exécutés, la base de données doit être supprimée
et le moteur de base de données arrêté.
C'est une tâche parfaite pour Docker, qui peut exécuter des systèmes complexes de manière isolée
avec une configuration minimale. Nous avons un choix ici : nous pouvons vouloir orchestrer la
création et la destruction de la base de données avec un script externe ou essayer de tout mettre en
œuvre dans la suite de tests. La première solution est celle que de nombreux frameworks utilisent, et
que j'ai explorée dans ma série de billets Flask Project Setup : TDD, Docker, Postgres and more³⁵,
donc dans ce chapitre je vais montrer une implémentation de cette solution. Comme je l'ai expliqué
dans les posts que j'ai mentionnés, le plan est de créer un script de gestion qui démarre et démonte
les conteneurs nécessaires, et exécute les tests entre les deux. Le script de gestion peut également
être utilisé pour exécuter l'application elle-même, ou pour créer des configurations de
développement, mais dans ce cas, je vais le simplifier pour ne gérer que les tests. Je vous
recommande vivement de lire ces articles si vous voulez avoir une vue d'ensemble de la
configuration que je vais utiliser. La première chose à faire si nous voulons utiliser Docker Compose
est d'ajouter l'exigence à Certains conteneurs Docker (comme celui de PostgreSQL que nous
utiliserons bientôt) dépendent des variables d'environnement pour effectuer la configuration initiale,
nous devons donc définir une fonction pour définir les variables d'environnement si elles ne sont pas
déjà initialisées. Nous définissons également quelques chemins pour les fichiers de configuration.
Comme nous avons défini la configuration de la base de données dans un fichier JSON, nous avons
besoin d'une fixture qui charge cette même configuration, afin que nous puissions nous connecter à
la base de données pendant les tests. Comme nous avons déjà le fichier Chapitre 06 Intégration avec
un système externe réel dans le script de gestion, nous avons juste besoin de l'intégrer. Il s'agit d'une
fixation qui n'est pas spécifique au référentiel Postgres, je l'introduirai donc dans tests/[Link]
Ce sont des données ou des configuration mise en place pour effectuer des test d’un logiciel
Grâce à la flexibilité de l'architecture propre, la prise en charge de plusieurs systèmes de stockage est
un jeu d'enfant.
Le dépôt MongoDB
La classe MongoRepo n'est évidemment pas la même que l'interface Postgres, car la bibliothèque
PyMongo est différente de SQLAlchemy, et la structure d'une base de données NoSQL diffère de celle
d'une base de données relationnelle.
Je pense que ce chapitre très bref a clairement montré les mérites d'une approche en couches et
d'une configuration de test appropriée. Jusqu'à présent, nous avons implémenté et testé une
interface vers deux bases de données très différentes comme PostgreSQL et MongoDB, mais les deux
interfaces sont utilisables par le même cas d'utilisation, ce qui signifie en fin de compte le même
point de terminaison de l'API.
• Créer une configuration appropriée pour Docker Compose et configurer les conteneurs
• Ajouter des commandes à [Link] qui nous permettent de contrôler les processus
Définissons les conteneurs que nous voulons utiliser dans notre environnement de production, et
comment nous voulons les connecter. Nous avons besoin d'une base de données prête pour la
production et j'utiliserai Postgres, comme je l'ai déjà fait pendant les tests. Ensuite, nous devons
envelopper Flask avec un serveur HTTP de production, et pour ce travail, j'utiliserai gunicorn. Enfin,
nous avons besoin d'un serveur Web pour agir en tant qu'équilibreur de charge. Le fichier
docker/[Link] contiendra la configuration de Docker Compose, selon la convention que
nous avons définie dans [Link]
Grâce à l'interface commune entre les référentiels, le passage de MemRepo, basé sur la mémoire, à
PostgresRepo est très simple. Il est clair que, comme la base de données externe ne contiendra pas
de données au départ, la réponse du cas d'utilisation sera vide. Tout d'abord, déplaçons l'application
vers le dépôt Postgres. La nouvelle version du point d'accès est Comme vous pouvez le voir, le
principal changement est que repo = MemRepo(rooms) devient repo = PostgresRepo(postgres_-
configuration). Un changement aussi simple est rendu possible par l'architecture propre et sa stricte
l'approche en couches. Le seul autre changement notable est que nous avons remplacé les données
initiales du référentiel basé sur la mémoire par un dictionnaire contenant les données de connexion,
qui proviennent des variables d'environnement définies par le script de gestion. Cela suffit pour que
l'application se connecte à la base de données Postgres que nous exécutons dans un conteneur, mais
comme je l'ai mentionné, nous devons également initialiser la base de données. Le strict minimum
dont nous avons besoin est une base de données vide avec le nom correct. N'oubliez pas que dans
cette configuration particulière, nous utilisons pour l'application une base de données différente
(APPLICATION_DB) de celle que le conteneur Postgres crée automatiquement au démarrage
(POSTGRES_DB). J'ai ajouté une commande spécifique au script de gestion pour effectuer cette tâche
Migrations
Nous avons besoin d'un moyen de créer les tables qui correspondent aux objets que nous avons
définis dans rentomatic/repository/post [Link]. La meilleure stratégie, lorsque nous utilisons un
ORM comme SQLAlchemy, est de créer et d'exécuter les migrations, et pour cela nous pouvons
utiliser Alembic³⁸. Si vous êtes toujours connecté avec psql, quittez avec \q, puis éditez
requirements/[Link] et ajoutez alembic.
QU'EST-CE QUE LE WRONG ?: Le wrong signifie incorrect ou erroné, souvent utilisé pour décrire des
résultats inattendus ou des erreurs dans le code
• "La course n'est pas pour les rapides, ni le combat pour les forts.
Le Lièvre, si confiant dans sa vitesse intrinsèque, ne prend pas la course au sérieux et fait la sieste
pendant que la Tortue franchit la ligne d'arrivée. La seule façon d'aller vite, c'est d'aller bien.
Chaque système logiciel offre deux valeurs différentes aux parties prenantes : le comportement et la
structure. Les développeurs de logiciels sont chargés de veiller à ce que ces deux valeurs restent
élevées.
La première valeur d'un logiciel est son comportement. Les programmeurs sont engagés pour faire
en sorte que les machines se comportent de manière à faire gagner ou économiser de l'argent aux
parties prenantes. Pour ce faire, nous aidons les parties prenantes à élaborer une spécification
fonctionnelle ou un document d'exigences. Nous écrivons ensuite le code qui permet aux machines
des parties prenantes de répondre à ces exigences.
L'ARCHITECTURE
La deuxième valeur du logiciel est liée au mot "software", un mot composé de "soft" et "ware". Le
mot "ware" signifie "produit" ; le mot "soft"... C'est là que réside la deuxième valeur.
Si vous posez la question aux chefs d'entreprise, ils vous diront souvent qu'il est plus important que
le système logiciel fonctionne. Les développeurs, quant à eux, sont souvent d'accord avec cette
attitude. Mais ce n'est pas la bonne attitude. Je peux prouver qu'elle est erronée à l'aide d'un outil
logique simple qui consiste à examiner les extrêmes.
• Si vous me donnez un programme qui fonctionne parfaitement mais qui est impossible à modifier,
il ne fonctionnera pas lorsque les exigences changeront et je ne serai pas en mesure de le faire
fonctionner. Par conséquent, le programme deviendra inutile.
• Si vous me donnez un programme qui ne fonctionne pas mais qui est facile à modifier, je peux le
faire fonctionner et le maintenir en fonction de l'évolution des besoins. Le programme restera donc
utile en permanence.
La matrice Eisenhower : J'ai deux sortes de problèmes, les urgents et les importants. Les urgents ne
sont pas importants, et les importants ne sont jamais urgents.
La première valeur du logiciel - le comportement - est urgente mais pas toujours très importante. La
deuxième valeur du logiciel - l'architecture - est importante mais jamais particulièrement urgente.
Bien sûr, certaines choses sont à la fois urgentes et importantes. D'autres ne sont ni urgentes ni
importantes. En fin de compte, nous pouvons classer ces quatre couplets par ordre de priorité : 1.
Urgent et important 2. Pas urgent ni important 3. Urgent et pas important 4. Pas urgent et pas
important Notez que l'architecture du code - les choses importantes - se trouve dans les deux
premières positions de cette liste, tandis que le comportement du code occupe les premières et
troisièmes positions.
Décomposition Functional
Les test :
"Les tests montrent la présence, et non l'absence, de bogues". En d'autres termes, un test peut
prouver qu'un programme est incorrect, mais il ne peut pas prouver qu'il est correct. Tout ce que les
tests peuvent faire, après un effort de test suffisant, c'est nous permettre de considérer qu'un
programme est suffisamment correct pour nos besoins.
Ces preuves d'incorrection ne peuvent être appliquées qu'à des programmes prouvables. Un
programme qui n'est pas prouvable - en raison d'une utilisation non limitée de goto, par exemple - ne
peut pas être considéré comme correct, quel que soit le nombre de tests qui lui sont appliqués.
La capitulation :
Les langages OO permettent d'encapsuler facilement et efficacement les données et les fonctions.
Par conséquent, une ligne peut être tracée autour d'un ensemble cohérent de données et de
fonctions. En dehors de cette ligne, les données sont cachées et seules certaines fonctions sont
connues. Nous voyons ce concept à l'œuvre dans les membres privés et les fonctions publiques d'une
classe.
Le polymorphisme :
Le polymorphisme est un concept fondamental de la programmation orientée objet qui permet à une
fonction ou méthode de se comporter différemment en fonction du type d’objet ou de données sur
lequel elle est appelée.
Le polymorphisme est une application des pointeurs sur les fonctions. Les programmeurs utilisent
des pointeurs sur des fonctions pour obtenir un comportement polymorphe depuis que les
architectures de Von Neumann ont été mises en œuvre pour la première fois à la fin des années
1940. En d'autres termes, l'OO n'a rien apporté de nouveau.
Dépendency inversion
L'OO est la capacité, grâce à l'utilisation du polymorphisme, d'obtenir un contrôle absolu sur chaque
dépendance du code source dans le système
Elle permet à l'architecte de créer une architecture enfichable, dans laquelle les modules qui
contiennent des politiques de haut niveau sont indépendants des modules qui contiennent des
détails de bas niveau.
PROGRAMME FUNCTIOHAL
Carrées integers :Ce sont des fonctions qui peuvent être intégrer sur un intervalle donné ? c’est-à-
dire des fonction pour laquelle l’intégrale existe et est finie
• Cette liste est transmise à la fonction map, qui appelle la fonction anonyme de quadrillage sur
chaque élément, produisant ainsi une nouvelle liste interminable de tous les carrés.
• La liste des carrés est transmise à la fonction take, qui renvoie une nouvelle liste contenant
uniquement les 25 premiers éléments.
• La fonction println imprime son entrée, qui est une liste d e s 25 premiers carrés d'entiers.
Le programme clojure est un langage de programmation fonctionnel ,conçu pour être utilisé avec la
machine virtuelle Java .Clojure est connu pour sa simplicité, sa concision et sa capacité à faciliter la
programmation concurrente.
Immutabilité et architecture
Les applications qui nécessitent plusieurs threads et plusieurs processeurs - ne peuvent pas se
produire s'il n'y a pas de variables mutables. l'immuabilité peut être réalisable, à condition de faire
certains compromis.
Séparation de mutabilité
L'un des compromis les plus courants en matière d'immutabilité consiste à séparer l'application, ou
les services au sein de l'application, en composants mutables et immuables. Les composants
immuables exécutent leurs tâches de manière purement fonctionnelle, sans utiliser de variables
mutables. Les composants immuables communiquent avec un ou plusieurs autres composants qui ne
sont pas purement fonctionnels et permettent de modifier l'état des variables.
Les limites de la puissance de stockage et de traitement ont rapidement disparu. De nos jours, il est
courant que les processeurs exécutent des milliards d'instructions par seconde et disposent de
milliards d'octets de mémoire vive. Plus nous disposons de mémoire et plus nos machines sont
rapides, moins nous avons besoin d'un état mutable.
• La programmation structurée est une discipline imposée lors d'un transfert direct de contrôle.
• La programmation orientée objet est une discipline imposée au transfert indirect de contrôle.
Les principes SOLID nous indiquent comment organiser nos fonctions et nos structures de données
en classes, et comment ces classes doivent être interconnectées.
L'objectif de ces principes est de créer des structures logicielles de niveau intermédiaire qui :
• Tolérer le changement,
• Ils constituent la base des composants qui peuvent être utilisés dans de nombreux systèmes
logiciels.
Les chapitres suivants décrivent chaque principe plus en détail. Voici le résumé :
• LSP : Le principe de substitution de Liskov La célèbre définition des sous-types de Barbara Liskov,
datant de 1988. En résumé, ce principe dit que pour construire des systèmes logiciels à partir de
pièces interchangeables, ces pièces doivent adhérer à un contrat qui permet de les substituer l'une à
l'autre.
• ISP : Principe de ségrégation des interfaces Ce principe conseille aux concepteurs de logiciels
d'éviter de dépendre de choses qu'ils n'utilisent pas.
• DIP : Le principe d'inversion de la dépendance Le code qui met en œuvre la politique de haut
niveau ne doit pas dépendre du code qui met en œuvre les détails de bas niveau. Au contraire, les
détails doivent dépendre des politiques.
De tous les principes SOLID, le principe de responsabilité unique (SRP, single responsibility principle))
est peut-être le moins bien compris. C'est probablement parce que son nom est particulièrement
inapproprié. Selon ce principe une classe ou un module ne doit avoir qu’une seule raison de changer,
ce qui signifie qu’une classe ne doit avoir qu’une seule responsabilité ou fonctionnalité.
Ne vous y trompez pas, il existe un principe de ce type. Une fonction ne doit faire qu'une seule et
unique chose.
. Les systèmes logiciels sont modifiés pour satisfaire les utilisateurs et les parties prenantes ; ces
utilisateurs et parties prenantes sont la "raison de changer" dont parle le principe. En effet, nous
pouvons reformuler le principe comme suit :
Un module doit être responsable devant un, et un seul, utilisateur ou partie prenante
La version finale de l'ASR est donc la suivante :
SYMPTÔME 2 : Fusions
Résolution :
Il existe de nombreuses solutions à ce problème. Chacune déplace les fonctions dans des classes
différentes. La façon la plus évidente de résoudre le problème est peut-être de séparer les données
des fonctions. Les trois classes partagent l'accès à EmployeeData, qui est une structure de données
simple sans méthodes (figure 7.3). Chaque classe ne détient que le code source nécessaire à sa
fonction particulière. Les trois classes ne sont pas autorisées à s e connaître. Ainsi, toute duplication
accidentelle est évitée.
En resumé :
Le principe de responsabilité unique concerne les fonctions et les classes, mais il réapparaît sous une
forme différente à deux autres niveaux. Au niveau des composants, il devient le principe de
fermeture commune. Au niveau de l'architecture, il devient l'axe de changement responsable de la
création des limites architecturales. Nous étudierons toutes c e s idées dans les chapitres à venir.
Cette principe stipule qu’une entité de logiciel doit être ouverte à l’extension mais fermée à la
modification
L'OCP est l'une des forces motrices de l'architecture des systèmes. L'objectif est de rendre le système
facile à étendre sans que l'impact du changement ne soit trop important. Cet objectif est atteint en
divisant le système en composants et en organisant ces composants selon une hiérarchie de
dépendance qui protège les composants de niveau supérieur des modifications apportées aux
composants de niveau inférieur.
PSL (Preuve de Système Logiciel) en informatique fait référence à une méthode ou un ensemble de
techniques utilisées pour garantir la fiabilité, la sécurité et la correction des logiciels par des preuves
formelles. L'idée principale est de démontrer, par des méthodes mathématiques, que le logiciel
fonctionne comme attendu en toutes circonstances.
La PSL peut et doit être étendue au niveau de l'architecture. Une simple violation de la substituabilité
peut entraîner la pollution de l'architecture d'un système par un nombre important de mécanismes
supplémentaires.
Le FSL est applicable parce qu'il y a des utilisateurs qui dépendent d'interfaces bien définies et de la
substituabilité des implémentations de ces interfaces.
ISP et language
Il est clair que la description donnée précédemment dépend essentiellement du type de langage. Les
langages à typage statique comme Java obligent les programmeurs à créer des déclarations que les
utilisateurs doivent importer, utiliser ou inclure d'une autre manière. Ce sont ces déclarations
incluses dans le code source qui créent les dépendances du code source qui forcent la recompilation
et le redéploiement. Dans les langages à typage dynamique comme Ruby et Python, ces déclarations
n'existent pas dans le code source. Elles sont déduites au moment de l'exécution. Il n'y a donc pas de
dépendances du code source qui obligent à une recompilation et à un redéploiement. C'est la
principale raison pour laquelle les langages à typage dynamique créent des systèmes plus flexibles et
moins étroitement couplés que les langages à typage statique. Ce fait pourrait vous amener à
conclure que l'ISP est un problème de langue, plutôt qu'un problème d'architecture.
Si l'on prend un peu de recul et que l'on examine les motivations profondes de l'ISP, on s'aperçoit
qu'un problème plus profond se cache là. En général, il est néfaste de dépendre de modules qui
contiennent plus que ce dont vous avez besoin. C'est évidemment vrai pour les dépendances du code
source qui peuvent imposer une recompilation et un redéploiement inutiles, mais c'est également
vrai à un niveau architectural beaucoup plus élevé.
Le principe d'inversion de la dépendance (DIP) nous indique que les systèmes les plus flexibles sont
ceux dans lesquels les dépendances du code source ne se réfèrent qu'à des abstractions et non à des
concrétions.
L'implication est donc que les architectures logicielles stables sont celles qui évitent de dépendre de
concrétions volatiles et qui favorisent l'utilisation d'interfaces abstraites stables. Cette implication se
résume à un ensemble de pratiques de codage très spécifiques :
• Ne faites pas référence à des classes concrètes volatiles. Faites plutôt référence à des interfaces
abstraites. Cette règle s'applique à tous les langages, qu'ils soient typés statiquement ou
dynamiquement. Elle impose également des contraintes sévères sur la création d'objets et impose
généralement l'utilisation de fabriques abstraites.
• Ne pas dériver de classes concrètes volatiles. Il s'agit d'un corollaire de la règle précédente, mais il
mérite une mention spéciale. Dans les langages à typage statique, l'héritage est la relation la plus
forte et la plus rigide de toutes les relations du code source ; par conséquent, il doit être utilisé avec
beaucoup de précautions. Dans les langages à typage dynamique, l'héritage pose moins de
problèmes, mais il s'agit toujours d'une dépendance, et la prudence est toujours le choix le plus
judicieux.
• Ne surchargez pas les fonctions concrètes. Les fonctions concrètes nécessitent souvent des
dépendances au niveau du code source. Lorsque vous remplacez ces fonctions, vous n'éliminez pas
ces dépendances - au contraire, vous en héritez. Pour gérer ces dépendances, vous devez rendre la
fonction abstraite et créer plusieurs implémentations.
• Ne jamais mentionner le nom de quelque chose de concret et de volatile. Il s'agit en fait d'une
simple reformulation du principe lui-même.
Les composants sont les unités de déploiement. Ce sont les plus petites entités qui peuvent être
déployées dans le cadre d'un système.
Délocalisation
La solution consistait à utiliser des binaires relocalisables. L'idée sous-jacente était très simple. Le
compilateur a été modifié pour produire un code binaire pouvant être relocalisé dans la mémoire par
un chargeur intelligent. Le chargeur devait savoir où charger le code relocalisable. Le code
relocalisable était doté de drapeaux qui indiquaient au chargeur quelles parties des données
chargées devaient être modifiées pour être chargées à l'adresse sélectionnée. En général, il s'agissait
simplement d'ajouter l'adresse de départ à toutes les adresses de référence de la mémoire dans le
binaire.
Le PCC amplifie cette leçon en regroupant dans un même composant les classes qui sont fermées aux
mêmes types de changements. Ainsi, lorsqu'un changement d'exigences survient, il a de bonnes
chances d'être limité à un nombre minimal de composants.
Rassemblez les éléments qui changent au même moment et pour les mêmes raisons. Séparez les
choses qui changent à des moments différents ou pour des raisons différentes.
Le principe de réutilisation commune (CRP) est un autre principe qui nous aide à décider quelles
classes et quels modules doivent être placés dans un composant. Il stipule que les classes et les
modules qui ont tendance à être réutilisés ensemble appartiennent au même composant.
La CRP est la version générique de l'ISP. L'ISP nous conseille de ne pas dépendre de classes qui ont
des méthodes que nous n'utilisons pas. La CRP nous conseille de ne pas dépendre de composants qui
ont des classes que nous n'utilisons pas. Ne dépendez pas de choses dont vous n'avez pas besoin.
LA CONSTRUCTION HEBDOMADAIRE
La construction hebdomadaire était courante dans les projets de taille moyenne. Elle fonctionne de
la manière suivante : Tous les développeurs s'ignorent pendant les quatre premiers jours de la
semaine. Ils travaillent tous sur des copies privées du code et ne se préoccupent pas de l'intégration
de leur travail sur une base collective. Puis, le vendredi, ils intègrent toutes leurs modifications et
construisent le système.
La stabilité d'un composant logiciel garantit que, même sous des conditions de stress, de
modifications ou d'usages intensifs, il continuera à fonctionner correctement sans introduire de bugs
ou d'anomalies dans le système. C’est un critère essentiel dans les systèmes critiques ou les logiciels
à long cycle de vie.
Modularité : Les composants peuvent être modifiés ou remplacés sans affecter le reste du
système, tant que l'interface reste la même.
Réutilisabilité : Un composant abstrait peut être réutilisé dans d'autres parties du
programme ou dans d'autres projets, car ses détails d'implémentation sont cachés.
Simplification : Cela permet de travailler à un niveau plus élevé sans se soucier des détails
d'implémentation.
Description : L'UI est la partie visible par l'utilisateur, permettant l'interaction avec le
logiciel. Elle peut inclure des boutons, des menus, des fenêtres, etc.
Exemples : Interface graphique (GUI) sur un ordinateur, interface en ligne de
commande (CLI), applications mobiles, etc.
2. Le code source
Description : Le code source est l'ensemble des instructions écrites par des
programmeurs dans des langages de programmation. Ce code décrit la logique et les
fonctionnalités du logiciel.
Exemples de langages : Java, Python, C++, JavaScript.
3. La base de données
4. Le serveur
Description : Un serveur exécute des logiciels ou héberge des applications et services
accessibles à distance par les utilisateurs via un réseau, souvent Internet.
Exemples : Apache, Nginx, serveurs de fichiers, serveurs d'applications.
7. Le middleware
Description : Le middleware est un logiciel qui fait le lien entre les différentes
applications ou entre une application et un système d'exploitation.
Exemples : Serveurs d'application, bus de messages, systèmes de gestion des
transactions.
8. Le système d'exploitation
Description : C'est le logiciel de base qui gère le matériel et les autres logiciels sur un
ordinateur.
Exemples : Windows, Linux, macOS.
Le déploiement :
La stratégie de déploiement est rarement prise en compte lors du développement initial. Cela
conduit à des architectures qui peuvent rendre le système facile à développer, mais qui le rendent
très difficile à déployer. Cependant, au moment de déployer le système, ils peuvent découvrir que le
nombre de micro-services est devenu impressionnant ; la configuration des connexions entre eux et
le moment de leur lancement peuvent également s'avérer être une énorme source d'erreurs.
Si les architectes avaient pris en compte les questions de déploiement dès le début, ils auraient peut-
être opté pour moins de services, un hybride de services et de composants en cours de traitement, et
un moyen plus intégré de gérer les interconnexions.
Cela dit, l'architecture joue un autre rôle dans le fonctionnement du système : Une bonne
architecture logicielle communique les besoins opérationnels du système.
Courrier Junk
Comme nous l'avons déjà dit, une bonne architecture doit soutenir :
• La maintenance du système.
• Le développement du système.
• Le déploiement du système.
Exploitation :
Si le système doit gérer 100 000 clients par seconde, l'architecture doit supporter ce type de débit et
de temps de réponse pour chaque cas d'utilisation qui l'exige. Si le système doit interroger des cubes
de données en quelques millisecondes, l'architecture doit être structurée de manière à permettre ce
type de fonctionnement.
Développement :
Toute organisation qui conçoit un système produira une conception dont la structure est u n e copie
de la structure de communication de l'organisation.
Le système doit être correctement divisé en composants bien isolés et pouvant être développés
indépendamment les uns des autres. Ces composants peuvent ensuite être attribués à des équipes
qui peuvent travailler indépendamment les unes des autres.
Le déploiement :
Une bonne architecture permet au système d'être déployé immédiatement après sa construction.
Description : Il s'agit de séparer les aspects techniques d'une application, comme l'interface
utilisateur, la logique métier et les données. Cela conduit souvent à des architectures de type
MVC (Modèle-Vue-Contrôleur) ou à l’utilisation de couches logicielles.
Exemple : Dans une architecture MVC, la partie "Modèle" gère les données, la "Vue" gère
l'affichage à l'utilisateur, et le "Contrôleur" orchestre les interactions entre les deux.
3. Découplage temporel
Description : Ce type de découplage concerne les interactions entre composants qui n'ont
pas besoin de se produire simultanément. Les communications peuvent être asynchrones,
permettant à un composant d'envoyer un message et de continuer son exécution sans
attendre une réponse immédiate.
Exemple : L’utilisation de files d’attente ou de messages pour la communication entre
services dans une architecture de microservices. Le service d’envoi d’emails peut ajouter un
message dans une file d’attente, et un autre service traitera cet email plus tard.
4. Découplage physique
Description : Il s'agit de séparer physiquement les composants d'un système. Cela signifie
que différentes parties du système sont déployées sur des machines ou des serveurs
différents.
Exemple : Dans une architecture microservices, chaque service est indépendant et déployé
séparément, souvent dans des conteneurs (ex : Docker). Ainsi, si un service tombe en panne
ou doit être mis à jour, cela n'affecte pas les autres.
Techniques de découplage
1. Interfaces et abstractions
3. Événements et observateurs
4. Middleware et messages
Description : Les messages ou les queues sont souvent utilisés pour découpler les systèmes,
en particulier dans des architectures distribuées. Cela permet à des composants de
communiquer de manière asynchrone et sans dépendance directe.
Exemple : Utilisation d'outils comme RabbitMQ ou Kafka pour envoyer et recevoir des
messages entre des microservices.
5. API et microservices
Description : Les API sont des points de communication bien définis qui permettent à
différents composants ou services d’interagir sans dépendance directe. Dans une
architecture de microservices, chaque service expose une API indépendante, souvent basée
sur des protocoles comme HTTP/REST ou gRPC.
Exemple : Un service de gestion des utilisateurs expose une API REST pour les opérations
CRUD (Créer, Lire, Mettre à jour, Supprimer), qui peut être utilisée par différents modules
(authentification, profil, etc.).
Avantages du découplage
1. Maintenance simplifiée : En isolant chaque composant, il est plus facile de les mettre à jour
ou de les remplacer sans affecter tout le système.
2. Tests unitaires et de composants : Le découplage facilite les tests, car chaque composant
peut être testé individuellement en utilisant des bouchons ou des mocks pour simuler les
autres parties du système.
3. Flexibilité et évolutivité : Les systèmes découplés sont plus adaptables aux changements. Les
nouvelles fonctionnalités ou composants peuvent être ajoutés sans impacter le reste du
système.
4. Réutilisabilité : Un composant découplé est plus facilement réutilisable dans d’autres parties
du système ou dans d’autres projets.
5. Résilience : Dans un système découplé, une panne dans un composant n’affecte pas
nécessairement tout le système, améliorant ainsi sa résilience.
Dans une architecture microservices, chaque service est conçu pour être indépendant et
découplé des autres. Chaque service a sa propre base de données, sa propre logique métier, et
est déployé indépendamment. La communication entre les services se fait via des API (HTTP,
REST) ou des messages asynchrones.
Cette architecture permet de déployer, mettre à jour ou faire évoluer chaque service sans
affecter les autres, ce qui améliore la résilience, la scalabilité, et la gestion du cycle de vie du
logiciel.
Conclusion
Le découplage des cas d'utilisation et des couches permet également une grande flexibilité
dans le déploiement. En effet, si le découplage est bien fait, il devrait être possible de
remplacer à chaud les couches et les cas d'utilisation dans les systèmes en cours d'exécution.
Mode de découplage :
• Au niveau de la source. Nous pouvons contrôler les dépendances entre les modules du code
source de sorte que les modifications apportées à un module n'obligent pas à modifier ou à
recompiler les autres modules (par exemple, Ruby Gems).
Dans ce mode de découplage, les composants s'exécutent tous dans le même espace
d'adressage et communiquent entre eux à l'aide de simples appels de fonction. Un seul
exécutable est chargé dans la mémoire de l'ordinateur. On parle souvent de structure
monolithique.
• Niveau de déploiement. Nous pouvons contrôler les dépendances entre les unités
déployables telles que les fichiers jar, les DLL ou les bibliothèques partagées, de sorte que les
modifications apportées au code source d'un module n'obligent pas les autres à être
reconstruits et redéployés. De nombreux composants peuvent encore se trouver dans le même
espace d'adressage et communiquer par le biais d'appels de fonction. D'autres composants
peuvent se trouver dans d'autres p r o c e s s u s du même processeur et communiquer par le
biais de communications interprocessus, de sockets ou de mémoire partagée. Ce qui importe
ici, c'est que les
composants découplés soient divisés en unités déployables de manière indépendante, telles
que des fichiers jar, des fichiers Gem ou des DLL.
Cela peut être difficile à comprendre au début. Nous pensons souvent au comportement du
système en termes de comportement de l'OI. Prenons l'exemple d'un jeu vidéo. Votre
expérience est dominée par l'interface : l'écran, la souris, les boutons et les sons. Vous oubliez
que derrière cette interface se cache un modèle - un ensemble sophistiqué de structures de
données et de fonctions - qui la pilote. Plus important encore, ce modèle n'a pas besoin de
l'interface. Il s'acquitterait volontiers de ses tâches, en modélisant tous les événements du jeu,
sans que le jeu ne soit jamais affiché à l'écran. L'interface n'a pas d'importance pour le modèle
- les règles de gestion.
Composant de déploiement
Le processus local constitue une limite architecturale physique beaucoup plus solide. Un
processus local est généralement créé à partir de la ligne de commande ou d'un appel système
équivalent. Les processus locaux s'exécutent dans le même processeur ou dans le même
ensemble de processeurs au sein d'un système multicœur, mais dans des espaces d'adressage
distincts. La protection de la mémoire empêche généralement ces processus de partager la
mémoire, bien que des partitions de mémoire partagée soient souvent utilisées.
Pourquoi les entités sont-elles de haut niveau et les cas d'utilisation de niveau inférieur ? Parce que
les cas d'utilisation sont spécifiques à une application unique et sont donc plus proches des entrées
et sorties de ce système. Les entités sont des généralisations qui peuvent être utilisées dans de
nombreuses applications différentes, elles sont donc plus éloignées des entrées et sorties du
système. Les cas d'utilisation dépendent des entités ; les entités ne dépendent pas des cas
d'utilisation.
Les règles de gestion d'un système logiciel sont des principes et bonnes pratiques permettant
de garantir la qualité, la maintenabilité, la performance et la sécurité d'un logiciel tout au long
de son cycle de vie. Ces règles touchent à différents aspects, notamment la conception, le
développement, le déploiement, et la maintenance du logiciel. Voici les principales règles de
gestion à respecter dans un projet logiciel :
1. Conception et architecture logicielle
Contrôle de version : Utiliser un système de gestion des versions tel que Git pour
suivre les modifications du code, gérer les branches de développement et faciliter la
collaboration entre les développeurs.
Versionnement sémantique : Suivre un versionnement sémantique (ex. : 1.0.0)
permet d'indiquer clairement les changements majeurs, mineurs ou les corrections de
bugs.
3. Qualité du code
4. Tests et validation
Tests unitaires : Écrire des tests unitaires pour valider le comportement de chaque
composant du logiciel de manière indépendante.
Tests d'intégration : Effectuer des tests d'intégration pour vérifier que les modules
du système fonctionnent bien ensemble.
Tests automatisés : Utiliser des outils d'automatisation des tests pour tester
régulièrement le logiciel tout au long du processus de développement (CI/CD).
Tests de performance : Valider les performances du système pour s'assurer qu'il
répond aux besoins en termes de rapidité et d'efficacité.
5. Sécurité
Contrôle des accès : Gérer les droits d'accès et les niveaux d’autorisation pour
protéger les données et les fonctionnalités sensibles.
Chiffrement des données : Chiffrer les données sensibles, aussi bien en transit
(SSL/TLS) qu’au repos (cryptage de la base de données).
Validation des entrées : Protéger le système contre les attaques en validant
correctement toutes les entrées des utilisateurs pour éviter des attaques telles que
l'injection SQL ou le cross-site scripting (XSS).
6. Gestion des ressources
7. Maintenance et support
8. Automatisation et DevOps
Design centré sur l'utilisateur : Concevoir le système avec une interface utilisateur
intuitive et ergonomique pour offrir une bonne expérience à l'utilisateur final.
Accessibilité : S'assurer que l'application est accessible à tous les utilisateurs, y
compris ceux ayant des limitations physiques ou techniques (ex. : handicap, connexion
lente).
En résumé :
La gestion d'un système logiciel implique un ensemble de règles et de bonnes pratiques autour
de la conception, de la qualité du code, de la sécurité, des tests, de la maintenance et du
déploiement. Ces règles visent à garantir la robustesse, la scalabilité, et la pérennité du
logiciel tout en assurant une bonne expérience pour les utilisateurs et les développeurs.
Le web
L’architecture testable :
Si l'architecture de votre système est axée sur les cas d'utilisation, et si vous avez gardé vos
frameworks à distance, vous devriez être en mesure de tester tous ces cas d'utilisation sans aucun
des frameworks en place. Vous ne devriez pas avoir besoin que le serveur web fonctionne pour
exécuter vos tests. Vous ne devriez pas avoir besoin que la base de données soit connectée pour
exécuter vos tests. Vos objets Entité doivent être de simples objets qui ne dépendent pas de
frameworks, de bases de données ou d'autres complications. Vos objets de cas d'utilisation doivent
coordonner vos objets d'entité.
• BCE, introduit par Ivar Jacobson dans son livre Object Oriented Software Engineering : A Use-Case
Driven Approach
Chacune de ces architectures produit des systèmes qui présentent les caractéristiques suivantes :
• Testable. Les règles de gestion peuvent être testées sans l'interface utilisateur, la base de données,
le serveur web ou tout autre élément externe.
• Indépendant de l'interface utilisateur. L'interface utilisateur peut être modifiée facilement, sans
changer le reste du système. Une interface web peut être remplacée par une interface console, par
exemple, sans modifier les règles de gestion.
• Indépendant de la base de données. Vous pouvez remplacer Oracle ou SQL Server par Mongo,
BigTable, CouchDB ou autre. Vos règles commerciales ne sont pas liées à la base de données.
• Indépendantes de toute agence externe. En fait, vos règles de gestion ne savent rien du tout des
interfaces avec le monde extérieur.
Les ORM (Mappeur relationnel objet) seraient mieux nommés "mappeurs de données", car ils
chargent des données dans des structures de données à partir de tables de bases de données
relationnelles.
Une façon de construire une frontière partielle est de faire tout le travail nécessaire pour créer des
composants compilables et déployables indépendamment, puis de les garder ensemble dans le
même composant. Les interfaces réciproques sont là, les structures de données d'entrée/sortie sont
là, et tout est prêt - mais nous les compilons et les déployons tous en tant que composant unique.
Intégration continue (CI) : Avec CI, le code est régulièrement intégré et testé
automatiquement à chaque modification. Cela permet de détecter rapidement les bugs
et de garantir que le code est toujours en état de fonctionner.
Déploiement continu (CD) : Le déploiement continu permet de livrer
automatiquement en production après chaque validation réussie, garantissant que le
produit est toujours prêt à être déployé à tout moment.
5. Tests automatisés
Réunions régulières : Mettre en place des réunions régulières entre les équipes de
développement et de déploiement pour s'assurer que tout le monde est sur la même
longueur d'onde.
Documentation claire : Une documentation claire et à jour sur les spécifications, les
processus et les outils utilisés permet de réduire les malentendus et les erreurs pendant
le développement et le déploiement.
Responsabilité partagée : Encourager une culture où les deux équipes se sentent
responsables du succès du projet dans son ensemble, et non uniquement de leur partie
respective.
5. Tests automatisés
Surveillance continue : Mettre en place des outils de surveillance pour suivre les
performances de l'application en temps réel et détecter tout problème dès qu'il
survient.
Alertes et logs : Utiliser des systèmes de logs et d’alertes automatiques pour informer
les équipes de tout problème de performance ou d’erreur dans le système.
• "Les microprogrammes sont conservés dans des mémoires non volatiles telles que la
ROM, l'EPROM ou la mémoire flash. ([Link]
• "Un micrologiciel est un programme logiciel ou un ensemble d'instructions programmé
sur un dispositif matériel. ([Link]
• Un micrologiciel est un "logiciel (programmes ou données) qui a été écrit sur une
mémoire morte (ROM)". ([Link]
Le cadre
En informatique, "cadre" fait référence au cadre conceptuel ou aux frameworks utilisés pour
structurer et organiser le développement de logiciels ou de systèmes. Dans le contexte de
l'architecture informatique, il désigne la manière dont les différentes composantes d'un
système (matériel, logiciels, réseaux, etc.) sont organisées pour répondre aux besoins et aux
objectifs définis.
Voici quelques concepts clés liés à l'architecture informatique dans le cadre d'un système :
2. Frameworks
Frameworks web : tels que Django, Ruby on Rails, qui permettent de structurer le
développement d'applications web.
Frameworks d'architecture d'entreprise : TOGAF (The Open Group Architecture
Framework) qui aide à organiser la gestion des ressources informatiques dans les
grandes entreprises.
3. Cadres d'architecture
L'idée principale derrière une architecture en couches est de diviser les différentes
responsabilités du système en couches logiques qui s'empilent les unes sur les autres. Chaque
couche sert une fonction spécifique et a une interaction limitée avec d'autres couches. Les
couches peuvent inclure des aspects comme l'interface utilisateur, la logique métier, et l'accès
aux données.
Exemples d'utilisation
Applications web : Utilisation courante dans les frameworks comme Spring (Java) ou
[Link] (C#), où la séparation en couches permet de structurer l'application de
manière modulaire.
Architecture microservices : Les microservices peuvent utiliser une architecture en
couches au sein de chaque service pour structurer leur logique interne.
[Link] PointDeVente
Attributs:
o id : int
o adresse : chaîne
o geolocalisation : string
o niveauStock : Map<Produit, int>
o photo : string
o statistiques : Statistiques
Méthodes:
o creerPointDeVente()
o supprimerPointDeVente()
o suivreStock()
o ProfesqueCommande()
o veillerConcurrence()
o genererRapport()
[Link] Commercial
Attributs:
o id : int
o nom : string
o e-mail : chaîne
o role : string (superviseur, commercial)
o qrCode : string
o Objectifs : Liste
o taches : List<Tache>
Méthodes:
o planifierVisite()
o prospection()
o gererPortefeuille()
o suivreObjectifs()
o genererStatistiques()
o suivrePlansAction()
3. Classe Produit
Attributs:
o id : int
o nom : chaîne
o categorie : string
o prix : dou
Méthodes:
o ajusterNiveauStock()
[Link] Commande
Attributs:
o id :
o dateCommande : Date
o statut : string (en préparation, en livraison, livré)
o produits : List<Produit>
o pointDeVente : PointDeVente
Méthodes:
o passantCommande()
o suivreCommande()
[Link] Objectif
Attributs:
o id : int
o description : string
o dateDebut : Date
o dateFin : Date
o etat : string
Méthodes:
o Objectif suiv.
6.
Attributs:
o id : int
o description : string
o etat : string (à faire, en cours, terminé)
Méthodes:
o assignerTache()
o suiviTac
[Link] Visite
Attributs:
o id : int
o commercial : Commercial
o point
o questionnaire : List<Question>
o commentaire : string
Méthodes:
o commencerVisiter
o Questionnaire par le remplir
o Visite()
8. Classe Statistiques
Attributs:
o ventesJour : double
o ventesSemaine : double
o ventesMois : double
Méthodes:
o genererStatistiques()
[Link] Question
Attributs:
o id :
o séage : chaîne
o Repense : corde
Méthodes:
o poseQuestion()
Ce modèle vous permet de gérer les processus de création de points de vente, de gestion des
commerciaux, et des visites, ainsi que le suivi des commandes et des statistiques.
Table : point_vente
Table : produit
Table : stock
Table : alerte_stock
id_alerte (PK) : Identifiant unique de l’alerte.
id_stock (FK) : Référence au stock concerné.
date_alerte : Date à laquelle l’alerte a été déclenchée.
type_alerte : Type d’alerte (ex : seuil de stock bas, rupture de stock).
Table : commande
Table : reapprovisionnement
4. Veille concurrentielle
Table : activite_concurrentielle
Table : photo_concurrence
5. Reporting et statistiques
Table : vente
Table : rapport_vente
Table : branding
Table : photo_branding
Table : utilisateur
Table : affectation
Table : historique_performance
2. Prospection
Table : prospection
Table : visite
Table : portefeuille_commercial
Table : objectif_commercial
Table : suivi_objectif
Table : plan_action
Table : tache
8. Reporting et statistique
Table : rapport_commercial
Table : qr_code_commercial
Table : scan_qr_code