Thématiques principales

Affichage des articles dont le libellé est contraintes. Afficher tous les articles
Affichage des articles dont le libellé est contraintes. Afficher tous les articles

mercredi 28 mars 2018

Architectures types

Lorsque nous nous sommes intéressé au pattern DAO [1], nous avons pu voir que les systèmes logiciels se conçoivent en définissant une architecture répondant au mieux aux besoins clients en usant de différentes stratégies en terme de solutions techniques [2] (les patterns de conceptions, les frameworks, etc…) et de méthodes [3] (IDM, Agilité, Scrum, etc...).

Ainsi, au fils des articles nous avons croisé un certain nombre de patterns ou de framework qui clairement nous simplifie la vie pour produire notre application. Cependant ce genre de concepts se focalisent exclusivement sur le logiciel lui même sans fournir de réponses à des besoins environnementaux qui peuvent être nombreux et contraignants

Ainsi par exemple, que faire lorsque le logiciel doit être capable de gérer un taux de connexion élevé? Comment garantir la monté en charge? Comment s’assurer de l'intégrité des données ? de leur disponibilités ? de leur sauvegarde? Comment garantir qu’il n’y aura pas d'interruption de service? A toutes ces questions, le logiciel seul ne peut y répondre en plus du besoin pour lequel il est produit. En effet, si techniquement on peut imaginer répondre à ces problématiques, cela nécessiterait des développement spécifiques très lourd en plus du métier pour lequel le logiciel doit être fait et comme nous l’avons vu précédemment, toutes les approches purement logiciel sont beaucoup trop auto-centré.

A cette problématique existe heureusement un solution et celle-ci relève en fait du bon sens. Ainsi, si un logiciel doit être conçu et architecturé, son environnement doit l'être aussi! Et les choix d’architectures (interne et externe au logiciel) doivent permettre à ces deux systèmes de cohabiter.

Nous allons donc prendre un peu de recul sur la conception logiciel elle même et nous placer au dessus des patterns de conception que nous avons vu jusque ici pour regarder le logiciel de façon plus global. L’objectif est toujours de considérer le logiciel avec ses interfaces mais aussi de regarder au delà de celles ci pour penser l’architecture dans son ensemble.

Reprenons pour commencer l’exemple très simple de notre article sur le DAO [1]. Nous avions vu l’une des architectures les plus typiques, l’architecture en 3 couches permettant de dissocier les préoccupations métiers des préoccupations de représentations (la couche presentation) des préoccupations de stockage (la couches de données).

Il est évident que cette approche a de nombreux avantages, dans la conception d’une part car les spécificités des besoins des différentes couches seront isolés et confiés aux experts des différents domaines associés à ses couches. Dans la maintenance également car l'indépendance permet de substituer n’importe quelle couche par d’autres implémentations équivalentes ou même d’autres solutions techniques, tant que les interfaces entre couches sont respectées. Enfin ce genre d’approche faisant consensus parmis les développeurs, elle permet la mise en place de standard tant au niveau méthodologique qu’au niveau technique et aujourd’hui, parler par exemple de l’utilisation d’une architecture JEE en 3 couches fait tout de suite référence aux technologies JSP, JSF, Servlet pour la couche présentation, JPA, Hibernate pour les couches données tout en exploitant des EJB pour les couches métiers. Bien sur le modèle en 3 couches a ses limites et souvent il est necessaire de considerer quelques couches supplémentaires en ajoutant une couche de sécurité entre la présentation et le métier et une couche de cache entre le métier et la couche de données. Nous en obtenons alors le modèle en 5 couches [5].


Bien sur on peut en déduire des modèles à n couches mais cette approche a elle même des limites car en augmentant le nombre de couches, on augmente également le temps de traversé des flux de contrôle et de données, occasionnant une chute des performances.

Pour répondre à cette problématique, heureusement il existe également des solutions. Le problème de l’architecture en couche est d'être vertical. Ainsi, une solution est de fournir des fonctionnalités transverses déployées de manière horizontale.

De façon à concevoir des architectures plus horizontale, une approche dite orientée service est possible ou SOA [7]. Celle ci est apparue peu avant 2000 et a largement bénéficié de l'émergence des webservices. Le principe de SOA est simple, il se base sur le principe du producteur consommateur avec comme produit le service.

Pour orchestrer ces communications [8], un annuaire (UDDI) permet la définition d’un référentiel des services accessibles sur un bus de communication servant de middleware (souvent un ESB mais pouvant reposer plus simplement sur le Web si la sécurisation est complètement pris en compte). Enfin tous ces éléments vont reposer sur le standard xml pour faciliter l'interopérabilité de la communication et l'échange des données en restant conforme aux contrats définis pour les services en exploitant des support de communication comme SOAP ou JMS.

L'intérêt de ce genre d’architecture est clairement la mise en place d’un découplage entre les fonctions fourni par les services permettant à des équipes différentes de les développer indépendamment, sur des cibles potentiellement hétérogènes (OS, frameworks, language). de plus cela permet de faire vivre dans le même environnement des applications requièrant les même services ou partiellement. De même faire évoluer ce type de système se résume alors à l’identification des services à exploiter et des nouveaux à déployer.

La réside la difficulté de cette approche. Il faut être capable de dimensionner correctement les services de façon à ce qu’ils soient ni trop simple au risque d'être trop exploité avec des risques notable de perte de performances (à noter qu’un service est censé être unique donc c’est à lui de gérer l’exploitation de la demande et la monté en charge) soit trop complexe et donc trop peu réutilisable. Les architectures orienté service sont donc souvent confronté aux problèmes de performances qui sont directement relié aux capacités réseaux et aux capacités en temps de réponse des plus fragiles des services du système ( qui peuvent en ralentir ou d'encombrer drastiquement le fonctionnement) A noter que ce n’est pas le seul point noir car, si ls SOA favorise la modularité et le reuse des services, cela ne favorise pas son evoluabilité. Toutes transformations dans la chaîne des processus EBP (Enterprise Business Process) utilisant les services entraînera de forte perturbation sur l’ESB.

Pour palier à ces défauts les microservices proposent de descendre encore en granularité dans la décomposition fonctionnelle. Ainsi un microservice va se réduire à ne répondre qu'à une et une unique fonction tout en simplifiant les protocoles de communication par l’utilisation de REST par exemple et en supprimant le principe de bus. L’idée principale est de produire une fournir un plus grand nombre de services (en comparaison à SOA) mais plus simple, élémentaire et donc plus indépendant. Cela va alors faciliter leur maintenance et leur interchangeabilité.
Les microservices ont, au delà des aspects techniques, des avantages même au niveaux processus car grâce à leur indépendance, il est possible de morceler plus le système et donc de le rendre plus facilement testable. Cela conduit même au rapprochement du développeur dans cette phase de test qui aura la possibilité d’isoler complètement le service qu’il doit produire (on le voit arriver la le docker? ). Finalement, grâce à cela, l’utilisation des microservices va déplacer nos problématiques précédentes de performance et de maintenance en une problématique d’interconnexion et de logique de définition de processus métier. Attention alors à ne pas tomber dans des architectures plat de spaghetti…[6]

Jusque là nous nous sommes projetés dans des architectures sans parler de la structure réseau sous-jacente. Bien sur aujourd’hui une application ne se pense plus de façon stand alone sur un serveur, elles se pensent embarquées dans un ou plusieurs serveurs d’applications répartis sur plusieurs serveurs physiques et les approches de types SOA ou Microservices se prêtent parfaitement à cela mais si l’on ne considère pas forcément ce type de solutions, pourquoi malgré tout choisir une architecture distribuée et/ou décentralisé?

En réalité la réponse est simple, et c’est toujours la même, une architecture bien pensé doit permettre d'éviter de construire des monolithes immaintenables (remarque, si ca nous fait évoluer...) et la décomposition est souvent la première des bonnes idées à avoir.

Nous avons vu que ces principes font parti intégrante des architectures précédentes. En fait si les architectures distribuées et/ou décentralisées sont intéressantes c’est parce que non seulement elle poussent à décomposer le problème et favoriser ainsi la réutilisabilité, la maintenabilité (le problème étant localement plus simple du coup) mais elles permettent aussi d'intégrer des solutions techniques répondant à des problèmes plus spécifiques et techniques exigés par le client comme des exigences de disponibilités, de montées en charges, de vitesse de traitement, etc...

source: Architecture fédérée

Par contre, il ne faut pas croire que parce que l’on va considérer une architecture distribuée et ou décentralisé, les problèmes sont terminées car cela peut aussi être une fausse bonne idées dans certains cas. En effet, souvent ce genre d'architecture nécessite d'incorporer au mieux des outils spécifiques permettant la gestion du problème considéré mais selon les cas, il faudra penser à des mécanismes spécifiquement développées par l'équipe de développement. Dans ce genre de cas, on s’engage souvent sur un chemin complexe car ce genre d'architecture ne sont pas simple à gérer soit même. Par exemple, si nous considérons le cas d’une architecture orienté données qu’est l’architecture fédérée, elle a pour objet de permettre la mise en collaboration de différentes bases de données pour donner l’illusion qu’elle n’en forme qu’une tout en leur permettant de rester indépendantes. Si l’idée est pertinente, dans la pratique (dans le cas de fédérations faiblement couplé), elle nécessite de constituer des vue spécifiques dans chacunes des bases, des schémas supplémentaires (privé : base initiale , export : données présenté aux autres bases, import : données reconstitué des autres base) et des mécanismes de reconstruction. Ce genre d’approche est très complexe à mener et doit être refait pour à chaque intégration d’une nouvelle base dans la fédération. Il sera probablement plus simple de réaliser une migration des données vers des bases repensées plus globalement.

Alors bien sur le cas des architectures fédérées n’est pas à généraliser et elle peuvent aussi trouver leur applications dans certains cas bien étudié. Il ne faut pas oblitérer que ce genre d’architecture permettent aussi de gérer des problématiques de redondances ou de répartition de charge pour obtenir des services en haute disponibilité.

Pour conclure cet article déjà assez long, on statuera sur le fait que toutes les architectures décrites jusqu'ici sont plutôt classiques alors aujourd’hui, de nouvelles architectures font leur apparition pour intégrer de nouveaux types de besoins comme l'intégration de service dans le cloud, le traitement de données massives, le besoin de procéder à des prévisions ou des profils de ventes grâce à du Big Data ou du Machine learning. Nous ne rentrerons pas dans le détails de celles ci maintenant mais nous y reviendrons.

Références:

[1] https://un-est-tout-et-tout-est-un.blogspot.fr/2018/03/design-pattern-dao.html
[2] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/10/la-conception-logicielle.html
[3] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/12/la-modelisation.html
[4] https://un-est-tout-et-tout-est-un.blogspot.fr/p/java.html
[5] https://jobprod.com/quelques-principes-darchitecture-dapplis-web-javajee/
[6] https://dzone.com/articles/microservices-vs-soa-is-there-any-difference-at-al
[7] https://www.piloter.org/techno/support/SOA.htm
[8] https://openclassrooms.com/courses/implementez-une-architecture-orientee-services-soa-en-java/mprendrecequecestunesoa

vendredi 9 février 2018

SGBD-R : Normalisation

Nous avons vu dans l’article introductif divers concepts sur les SGBD-R et nous sommes arrivé à la notion primordiale de normalisation de bases de données relationnelles. Voyons en quoi cela consiste.

La normalisation

La normalisation de bases de données est une manière de la modéliser, concevoir et structurer. La normalisation est même une propriété pour une base de données (selon différents niveaux) car les systèmes de gestion de bases de données sont eux même conçus en partant du principe que les bases de données qu’ils auront à gérer seront normalisées. Ainsi normaliser une base va permettre:
  • réduction des erreurs et des temps de résolutions
  • l'écriture de requête plus simple
  • une meilleur intégrité des données (absence de redondance de l’information)
  • des tris plus rapides
  • des mises à jour plus rapide
  • des accès concurrents plus simple à gérer
La normalisation se fonde sur la notion de dépendances traduisant des contraintes sur les concepts de la base de données qui doit être structurée. Elle se présente comme une suite de forme normale appliquant des contraintes et des algorithmes. Il s’agit d’un processus de réduction de la relation universelle en un ensemble de relation présentant un minimum de redondance d’intra-relation et un maximum d'indépendance inter-relation.

La relation universelle est la relation regroupant l’ensemble des concepts à développer dans la base de données.

 

Le processus de normalisation se décompose en différentes phases permettant d'accéder à différentes formes normales de la base de données allant de 1 à 6.

Pour expliquer les différentes phases de normalisation, prenons un exemple. Considérons une relation universelle traitant de la gestion de projet dans un processus Scrum: 

 
 Celle ci se résume alors par:
RU(nomRelease, nomSprint, typeEquipier, nomEquipier, typeCeremonie, nomCeremonie, dateCeremonie, numBacklog, estimation, points, typeTache, definitionReady, etatTache, nomTache) 
avec typeCeremonie {RevueSprint, Melée, Retrospective,DemarrageSprint} 
avec typeEquipier {ScrumMaster, Developpeur, ProductOwner} 
avec typeTache{Fonctionnelle, Technique} 
avec etatTache {Pret, EnCours, Fini}
Cette liste de concepts n’est probablement pas complète et pour la construction de la base de données, peut être que tous les attributs déclarer ne seront pas utile. Nous le découvrirons au fur et à mesure de l’application des Formes Normales. Pour pouvoir appliquer les formes normales il faut préalablement élaborer le graphe des dépendances fonctionnelles [2] afin de déterminer quels sont les attributs qui déterminent les autres et si il faut en définir d’autres.
Une dépendance fonctionnelle est une association entre deux ensembles X et Y d'attributs d'une même relation pour laquelle pour tout X il existe un unique Y.
On obtient alors :
nomEquipier -> typeEquipier; 
nomEquipier -> nomTache; 
nomEquipier -> nomSprint; 
nomCeremonie -> typeCeremonie; 
nomCeremonie -> dateCeremonie; 
nomCeremonie -> nomSprint; 
nomTache -> estimation; 
nomTache -> points; 
nomTache -> definitionReady; 
nomTache -> etatTache; 
nomTache -> typeTache; 
nomTache -> numBacklog; 
nomSprint -> nomRelease; 
nomSprint -> numBacklog; 
nomRelease -> numBacklog;
un coup de dot la dessus (fichier.dot)
dot -Tpng -oDependencyG.png DependencyG.dot
et on obtient l’arbre suivant: Appliquons maintenant les différentes formes normales

Première forme normale (FN1)

La première forme normale définit que toutes les relations de la base doivent posséder des attributs sémantiquement atomique, c’est a dire que ceux ci ne sont pas eux même des relations. Si l’on prend notre Relation universelle à la lettre en considérant que celle-ci ne contient que des Attributs, alors oui celle ci est FN1.

Seconde forme normale (FN2)

La seconde forme normale impose d’une part d'être en première forme normale. D’autre part, pour être en seconde forme normale, tous les attributs de relations n’appartenant pas à une clé candidate est en dépendance totale de chaque clef candidate. Bien ceci introduit deux nouveaux concepts, celui de clé candidate et celui de dépendance totale. Une clé candidate est un ensemble d’attributs d’une relation garantissant:
  • irréductibilité ; c’est a dire qu’il n'existe pas de sous ensemble de la clé garantissant l’unicité (en gros la clé est minimale dans la relation)
  • l’unicité : c’est à dire que deux entrée de la relation ne peuvent avoir la même clé candidate.
La notion de dépendance totale définit une dépendance qui n’est ni triviale, ni partiel. C’est a dire que la dépendance X -> A est dite total s’il n’existe pas Y inclu dans X tel que Y -> A

A noter que les notions de dépendance trivial et partiel définissent respectivement:
  • X -> Y est dite partiel si elle n’est pas trivial et si il existe Z inclu dans X tel que Z -> Y
  • X -> Y est dite triviale si Y est inclue dans X
Il y a quelques soucis car nous n’avons pas défini de clefs dans notre relation universelle. Mais en s’appuyant sur notre graphe des dépendances, nous allons probablement réussir en définir quelques unes, en choisissant entre autre les éléments déterminant les autres…. donnant le résultat suivant:
RU(nomRelease, nomSprint, typeEquipier, nomEquipier, typeCeremonie, nomCeremonie, dateCeremonie, numBacklog, estimation, points, typeTache, definitionReady, etatTache, nomTache)
En définissant des clefs, nous ne sommes évidemment pas en FN2. Pour le devenir, il va falloir appliquer le théorème de Heath. Celui ci défini que pour une relation R telle que s’il existe A, B et C des ensembles d’attributs et A -> B alors il R vaut la jointure des relations associants A,B et A,C. A noter que si B et C sont reliés par une dépendance fonctionnelle, alors il sera préférable (c’est le théorème de Jorma Rissanen) de décomposer R en A,B et B,C afin de conserver toutes les dépendances. Ainsi par exemple on peut extraire une relation associée aux taches
RU_sansTache(nomRelease, nomSprint, typeEquipier, nomEquipier, typeCeremonie, nomCeremonie, dateCeremonie, numBacklog,nomTache) 
Tache(nomTache,estimation, points, typeTache, definitionReady, etatTache, numBacklog)
Si on fait la jointure RU_sansTache avec Tache, l'operation nous restitue la RU initiale. Bien sur nous n'avons pas encore définie la jointure. Nous y reviendrons lorsque l’on traitera la jointure. Maintenant est ce que Tache est FN2? Oui! Mais pas RU_sansTache.
Il faut donc continuer la décomposition, à partir du graphe, nous pouvons nous intéresser a l’attribut nomRelease que nous pouvons extraire dans une relation Release:
RU_sansTache(nomRelease, nomSprint, typeEquipier, nomEquipier, typeCeremonie, nomCeremonie, dateCeremonie, numBacklog,nomTache) 
Release(nomRelease, numBacklog)
Ok pour Release mais toujours pas pour RU_sansTache qui en fait n’a pas évolué car nomRelease est une clef (ou partie) candidate qui devient étrangère et numBacklog qui est toujours déterminé par le Sprint. Du coup on va justement décomposer de façon à extraire une relation Sprint. Nous obtenons alors les relations suivantes:
RU_sansTache( nomSprint, typeEquipier, nomEquipier, typeCeremonie, nomCeremonie, dateCeremonie,nomTache) 
Sprint(nomSprint, nomRelease, numBacklog)
On va poursuivre en décomposant les Cérémonies:
RU_sansTache( nomSprint, typeEquipier, nomEquipier,nomTache) 
Ceremonie(nomCeremonie,typeCeremonie, nomSprint, dateCeremonie)
Et on va finir par renommer notre reste de Relation universelle en Equipier afin d’obtenir l’ensemble de relations suivant:
Equipier (nomEquipier, typeEquipier,nomTache, nomSprint) 
Ceremonie(nomCeremonie, typeCeremonie, nomSprint, dateCeremonie) 
Sprint(nomSprint, nomRelease, numBacklog) 
Release(nomRelease, numBacklog)
Voilà, nous avons terminé notre décomposition en 4 relations en FN2. Et on commence à poindre le schéma de notre futur base de données relationnelles.

Troisième forme normale (FN3)

La troisième forme normale impose d’une part d'être en deuxième forme normale. D’autre part pour être en FN3 il est nécessaire que tout attribut n’appartenant a aucune clef candidate de la relation ne soit dépendant que de cette clef candidate. Donc si pour une relation R, et les dépendances X -> Y et Y -> Z avec X,Y, Z des ensembles d’attributs tel que seul X est clé candidate alors la Relation contenant X, Y et Z n’est pas en forme normale. Dans notre graphe, cela reviendrait à dire qu’un élément est déterminé par à la fois une clef et un autre élément référencé lui même par cette même clef, et c'est pas bien. A noter que dans notre exemple nos relations sont FN3 sauf la relation Sprint dont l’attribut numBacklog est déterminé par nomRelease et nomSpring, nomRelease étant lui même déterminé par ce dernier. Que faire? En fait, nous avons un défaut de conception. Car un backlog est un ensemble de taches. Cependant, sémantiquement, le backlog de sprint ou celui d’une release ne sont pas les mêmes choses. Ils contiennent tous deux des Taches mais celui des sprints est déterminé au lancement du sprint et est figé une fois defini alors que celui de la release, lui, sert de tampon et de traitement des taches (par le PO voulant les priorisés). Nous n’avons pas voulu aller trop loin initialement dans la modélisation mais cette distinction est nécessaire. Pour résoudre cela, on va donc définir une nouvelle relation :
Backlog (numBacklog, typeBacklog)
avec typeBacklog = {backlogR, backLogS}
et désormais les références au backlog dans les autres relations deviennent des clefs étrangères :
Sprint(nomSprint, nomRelease, numBacklog) 
Release(nomRelease, numBacklog) 
Tache(nomTache,estimation, points, typeTache, definitionReady, etatTache, numBacklog)
A noter que maintenant par une petite analyse de notre modèle, les relation Sprint et Release ne sont plus que des relations d’associations. Nous sommes enfin FN3.

La forme normale de Boyce-Codd

Cette forme normale est une forme équivalente aux FN2 et FN3 mais en éliminant quelques redondances supplémentaires et peut donc être utilisée en tant qu’alternative aux deux autres formes normales Sa seule limite est qu’il est possible d'être FN3 sans forcément être en FNBC ce qui potentiellement peut nous faire perdre des dépendances fonctionnelles… donc en cas limites, il faut revenir aux FN2 et FN3. Ainsi, une relation R est en forme normale de Boyce-Codd si et seulement si, pour toutes les dépendances X -> A tel que X un ensemble d’attributs inclus dans R et A un attribut de R, alors soit A appartient a X (dépendance triviale) ou X est une sur clef dans R (ou dit autrement, X clef candidate de R).

Conclusions

L'application des formes normales sur les SGBDR est une démarche indispensable pour éviter les incohérences structurelles mais aussi sémantiques. Généralement, les démarches mises en œuvres s'arrêtent à la forme normale de niveau 3 ou la FNBC car elles sont jugées le plus souvent suffisantes (et généralement elles le sont). Dans un prochain article nous nous attarderons sur les formes normales 4, 5 et 6 qui prolongent un peu plus loin la recherche d’anomalies dans notre SGBD.

Références

[1] https://fr.wikipedia.org/wiki/Forme_normale_(bases_de_donn%C3%A9es_relationnelles) [2] https://fr.wikipedia.org/wiki/D%C3%A9pendance_fonctionnelle [3] http://fsmrel.developpez.com/basesrelationnelles/normalisation/

dimanche 4 février 2018

SGBD-R : Introduction

Introduction

Le développement de systèmes logiciels fait intervenir de nombreux composants et concepts.  Ces éléments sont organisés généralement a l’aide d’architectures conçues afin de répondre à différents types besoins comme capturer les concepts métier au travers d’algorithmes, ou définir des IHM afin de rendre convivial l'utilisation de l’application. A l‘opposé des IHM, dans ces architectures (que nous détaillerons dans un autre article), nous trouverons de façon quasi systématique un service de persistance des données ou base de données (SGBD).

En effet, l’utilisation de SGBD dans les systèmes logiciel actuel est devenu quasi incontournable. Leur rôle qui dépasse aujourd’hui le simple rôle de sauvegardes s'étend sur des fonctions telles que la redondance ou la gestion décentralisé des données afin de fournir des services à haute disponibilité . A la base très structuré pour des besoins de cohérence dans les systèmes de gestion de base de données relationnelles,  l'émergence du nosql a fait apparaître des type de base plus rapide et capable d'absorber et restituer de très grande quantité de données.

Les approche NoSQL sont tres interessante mais seront le propos la aussi d’un autre article, ainsi, dans celui ci je vous propose de revenir sur les SGBD-R et de parcourir leur utilisations,  leur avantage et leur inconvénients et ce qui justifie encore largement leur emplois [1].

Les SGBD-R ou systeme de gestion de bases de données relationnelles sont des outils de stockages de l’information contenant deux composants

  • un outil de gestion
  • un base de données relationnelle

Logiciel de gestion

L’outils de gestion est un outil offrant un service transactionnel d'accès aux données via un langage de requetes (SQL étant le plus commun et connu) des services de gestion des utilisateurs, des droits (en lecture et/ou écriture) , de la configuration (adresse d’acces, port de connexion, config de cache, etc.). Bien sur l’architecture du logiciel de gestion de la base de données comporte divers composants comme un catalogue système ou un gestionnaire de reprise pour la gérer les pannes et l'intégrité des données mais ces éléments peuvent variés d’une SGBDR a un autre (pour entrer dans le détails il est préférable de consulter les documentation officielle comme celle de postgres [2], Oracle [3], MySQL [4] ou SqlServer [5])

La partie logiciel de gestion n’est pas la partie la plus intéressante (enfin sauf pour les spécialistes a spécifique à ces bases), ce qu’il faut par contre en retenir est sa composante transactionnelle dont l’objectif est de garantir l'intégrité des données (en terme de concurrence d'accès, etc.) En ces termes, les transactions sont alors dites ACID :

  • Atomique : une transaction a la fois
  • Coherent: une transaction ne doit permettre la corruption de la base de donnée ou violer des contraintes d'intégrités
  • Isolation : les modifications apportées par une transaction ne sont accessible qu'après validation de la transaction
  • Durabilité : les modifications apportées aux données doivent, après validation, etre recuperable meme apres une panne.

La base de données

Caché derrière le logiciel de gestion, la partie base de données est le conteneur qui va structurer l’ensemble des informations. Pour cela, la base de donnée va s’appuyer sur le modèle relationnel qui consiste en la définition et la manipulation de Relations constituées d’Attributs. Associé aux Relations seront construits des tuples (ou enregistrement) assimilables a des instances des Relations pour lesquelles pour chaques Attributs on disposera d’une valeur. Classiquement on représente les relations, attributs et tuples par un tableau.

Ainsi par exemple on peut définir la Relation Personne constitué des Attributs Nom et Prenom pour lesquelles on va definir les tuples {Einstein, Albert} et {Turing, Alan} représenté comme suit:

Personne
Nom
Prenom

Einstein
Albert

Turing
Alan

Bien sur on aura un probleme si deux personnes ont les meme Nom et Prenom, c’est a dire les memes attributs, on va donc utiliser une clef primaire afin de permettre la différenciation. Cette clef primaire nommé id par convention, est définie comme unique (souvent auto-incrémenté), ceci est une contrainte d'intégrité

Personne
Id
Nom
Prenom

1
Einstein
Albert

2
Turing
Alan

Les contraintes d’intégrités

Des contraintes d'intégrations [6] il en existe 4 types. Nous venons d’en voir une, la contrainte structurelle, permettant un fonctionnement nominal de la base. Ensuite il est possible de définir des contraintes fonctionnelles comme une limite sur une valeur (comme un âge) permettant une capture plus fine du domaine métier que représente la base de données (nous reviendrons sur ce point plus précisément par la suite) Ensuite, il y aura des contraintes intra-relation, comme d’imposer la non nullité de l'attribut Nom dans la relation Personne. Ceci est une contrainte qui pourrait être vu comme fonctionnelle effectivement elle permet à l’enregistrement d’avoir du sens pour le domaine métier mais elle permet aussi de garantir un besoin technique (comme le calcule d’un jointure que nous verrons plus tard)

Enfin dernier type de contrainte, la contrainte inter-relation, qui va garantir une certaine coherence des données entre des relations différentes. Par exemple imaginons qu’il existe en plus de notre relation Personne, une relation adresse contenant les attributs Rue, CodePostal et Ville. Une contrainte pourrait définir que pour tout enregistrement Personne, il existe au moins une adresse connue.

Pour ce type de contrainte, il suffira par exemple de définir une clef étrangère dans la relation personne qui sera non null et dont la valeur sera la clef primaire de la relation Adresse:

Personne
IdPersonne
Nom
Prenom
IdAdresse

1
Einstein
Albert
1

2
Turing
Alan
2

Adresse
IdAdresse
Rue
CodePostal
Ville

1
xxx
59000
Lille

2
yyy
59500
Douai

Bien sur si cette façon de faire permet de répondre a la contrainte inter-relation, elle ne permet de resoudre le probleme du au moins une adresse, signifiant que potentiellement, une Personne peut avoir plusieur adresse. Ici c’est impossible.

Pour resoudre ce probleme, il va falloir inverser la logique, c’est a dire de référencer la Personne aupres des adresses ainsi on va pouvoir avoir plusieurs adresse référençant la même personne:

Personne
IdPersonne
Nom
Prenom

1
Einstein
Albert

2
Turing
Alan

Adresse
IdPersonne
IdAdresse
Rue
CodePostal
Ville

1
1
xxx
59000
Lille

2
2
yyy
59500
Douai

2
3
yyy
80000
Amiens

Bien sûr ceci ne résout pas un dernier potentiel problème qu’est : et si une adresse correspond a plusieurs personnes? (dans le cas d’une famille par exemple). Alors il est vrai que le plus évident est alors de dupliquer les adresses tout simplement comme elles auront des Id differentes mais si ils habitent au même endroit, a priori, cela ne sera pas compliqué de retrouver les enregistrements commun… Ceci est probablement l’approche la plus simple dans un cas comme celui la mais, on duplique des données et si les enregistrements ne sont pas renseigné exactement correctement, alors il y aura peut être des problèmes.

Pour resoudre ce probleme, on va créer une nouvelle relation dont la vocation sera de réifier l’association n-n que l’on cherche a creer (jusque la en fait sans le dire on examinait comment créer des associations 1-n et n-1 entre les relations Personne et Adresse) Ainsi on va créer une troisième relation que l’on nommera AssPersonneAdresse contenant juste les Id des différentes :

Personne
IdPersonne
Nom
Prenom

1
Einstein
Albert

2
Turing
Alan

Adresse
IdAdresse
Rue
CodePostal
Ville

1
xxx
59000
Lille

2
yyy
59500
Douai

3
yyy
80000
Amiens

AssPersonneAdresse
IdPersonne
IdAdresse

1
1

2
2

2
3

Par les questions que nous venons de traiter, on se rend facilement compte que créer une base de données nécessite une certaines expertise et de la méthode. Pour cela, on abordera la notion de normalisation de base de données dans un article prochain

Références

[1] https://dzone.com/articles/selecting-an-application-database?edition=305102&utm_source=Daily%20Digest&utm_medium=email&utm_campaign=dd%202017-06-16
[2] https://www.postgresql.org/
[3] https://docs.oracle.com/en/database/oracle/oracle-database/index.html
[4] https://www.mysql.com/fr/
[5] https://www.microsoft.com/fr-fr/sql-server/sql-server-2017
[6] http://www-inf.int-evry.fr/cours/BD/MSCurriculum/FRENCH/PDF/CI.pdf