Bases de données relationnelles et développement itératif

19

Dans de nombreuses approches de développement de logiciels comme les méthodologies agiles, la conception pilotée par domaine et l'analyse et la conception orientées objet, nous sommes encouragés à adopter une approche itérative du développement.

Nous ne sommes donc pas censés réussir notre modèle de domaine la première fois que nous commençons à travailler sur le projet. Au lieu de cela, au fil du temps, nous refactorisons le modèle parce que nous acquérons une compréhension plus profonde du domaine problématique avec le temps.

En dehors de cela, même si nous essayons d'obtenir un modèle parfait dès le départ, dont je suis déjà convaincu qu'il est très difficile, les exigences peuvent changer. Ainsi , après que le logiciel a été déployé à la production, les utilisateurs finaux peuvent remarquer qu'une certaine exigence n'a pas été complètement compris, ou pire, une exigence qui manquait.

Le point ici est que nous pourrions finir par avoir besoin de changer le modèle après le déploiement du logiciel. Si cela se produit, nous avons un problème: la base de données de production contient des données d'utilisateur qui sont importantes et sont déjà adaptées au format de l'ancien modèle .

La mise à jour du code peut être une tâche difficile si le code n'est pas bien conçu et si le système est volumineux. Mais cela peut se faire avec le temps, nous avons des outils comme Git qui nous aident à le faire sans endommager la version prête pour la production.

D'un autre côté, si le modèle change, si les propriétés des classes disparaissent ou autre, la base de données devrait également changer. Mais nous avons un problème: il y a déjà des données qui ne peuvent pas être perdues, qui sont déjà formatées pour l'ancien modèle.

Il semble qu'une base de données relationnelle soit ici un obstacle nous empêchant de faire du développement itératif et même de mettre à jour les logiciels lorsque les utilisateurs finaux le demandent.

Une approche que j'ai déjà utilisée consistait à coder une classe spéciale qui mappe les anciennes tables de base de données aux nouvelles. Ces classes sélectionnent donc les données dans l'ancien format, les convertissent au format utilisé par le nouveau modèle et les enregistrent dans les nouvelles tables.

Cette approche ne semble pas être la meilleure. Ma question est la suivante: existe-t-il des approches bien connues et recommandées pour concilier le développement itératif avec les bases de données relationnelles?

user1620696
la source
6
Soit dit en passant, je ne pense pas que cela ait quelque chose à voir avec les bases de données relationnelles en particulier. J'ai un problème similaire avec un projet sur lequel je travaille, mais nous l'avons avec le schéma de nos chaînes JSON qui représentent des objets très non relationnels. Il affecte probablement également toutes les formes de persistance.
Ixrec
1
Vous modifiez le schéma de la base de données de manière à ne pas perdre de données, en.wikipedia.org/wiki/Schema_migration .
RemcoGerlich
1
Je suis sûr que ce sujet a été longuement discuté quelque part auparavant, je ne le trouve pas sur les programmeurs. Mais voir ici martinfowler.com/articles/evodb.html ou ici stackoverflow.com/questions/334059/…
Doc Brown
1
"En dehors de cela, même si nous essayons d'obtenir un modèle parfait dès le départ, dont je suis déjà convaincu qu'il est très difficile, les exigences peuvent changer." Je voudrais ajouter que vous ne devriez même pas essayer d'obtenir un modèle (proche de la perfection) à l'avance. Cela pourrait lier votre état d'esprit à un type de solutions au lieu de laisser vos options ouvertes.
Bent

Réponses:

15

Il ne doit pas nécessairement s'agir de classes spéciales, mais oui, vous avez besoin de quelque chose qui prendra la base de données dans le format précédent et la convertira dans la version actuelle.

La chose ici est que vous devez développer un processus pour écrire et tester ces scripts et une discipline pour ne jamais toucher les bases de données de test et de production à la main, mais toujours par des scripts de migration.

Chaque fois que vous devez apporter une modification à la base de données, vous écrivez un script qui le fera, que ce soit en SQL ou en utilisant votre couche ORM, et vous le validerez dans votre contrôle de version avec les modifications qui nécessitent le nouveau schéma. Ensuite, vous disposez d'un script de contrôle qui mettra à niveau la base de données en appliquant tous les scripts de migration qui n'ont pas encore été appliqués dans une séquence.

Et assurez-vous de ne modifier que les environnements de développement, de test et d'assurance qualité partagés en appliquant les scripts et en revenant à la version précédente s'ils ne fonctionnent pas, de sorte que vous pouvez être raisonnablement sûr qu'ils fonctionneront comme prévu lorsque vous les libérez sur la production .

La nouvelle installation se fait simplement en appliquant tous les scripts. Après un certain temps, vous en aurez peut-être des centaines et penserez que c'est très inefficace, mais ne tombez pas dans le piège d'essayer de l'optimiser. L'installation est une activité ponctuelle et la maintenir fiable l'emporte sur la rapidité.

@Doc Brown a déjà lié Martin Fowler: Evolutionary Database Design et /programming/334059/agile-development-and-database-changes , et j'ajouterais Alex Papadimoulis: Database Changes Done Right , qui est plus court et a quelques exemples.

Comme exemple décent d'outil mettant en œuvre un tel processus, je suggère Alembic . Il est basé sur le framework Python SQLAlchemy , mais vous pouvez l'utiliser avec d'autres langages et frameworks s'ils n'ont pas leur propre support de migration. La page Wikipedia sur la migration de schéma répertorie d' autres outils de ce type .

Jan Hudec
la source
1
@Tibo vous construisez le schéma à partir de zéro en exécutant la même séquence de scripts. Voilà comment vous gérez le problème. Étant donné que, en tant que norme, vous pouvez accéder à partir de n'importe quelle instance de la base de données - y compris une qui n'existe pas encore - à un schéma actuel et avoir la certitude que c'est la même chose. Il n'est pas nécessaire d'avoir deux manières selon votre exemple. (Au moins pas donné une base de référence cohérente - la première étape consiste à établir la base de référence et une fois que vous atteignez cette base de référence, le problème disparaît.)
Murph
1
Bravo pour l'article d'Alex; il n'est peut-être pas plus court, mais il rend la lecture beaucoup plus orientée vers la pratique et divertissante.
Murphy
1
Nous sommes une boutique Agile et nous gérons un service à 100% de disponibilité et les deux s'appliquent également à la base de données. Nous migrons le schéma de production en moyenne une fois par jour et j'appuierais tout ce que Jan a dit. Une autre chose que nous faisons et qui a été inestimable est ce que nous appelons les tests de migration, cela s'exécute dans le cadre de notre processus de génération et de déploiement. Il prend un instantané de schéma hors production, lui applique toutes les migrations en attente du maître, puis exécute les tests unitaires du code de production actuellement déployé sur ce schéma. L'objectif est de vérifier que l'application des migrations ne cassera pas le système en cours d'exécution.
Gordon Wrigley
1

Curieusement, c'est le problème même auquel est confrontée mon équipe de développement actuelle. La question contient plusieurs sous-questions, elles seront donc traitées indépendamment.

D'abord et avant tout, une base de données relationnelle contraint-elle trop le modèle de données, rendant les changements très difficiles?

Certainement , mais pas nécessairement pour les raisons citées. Malheureusement, la polyvalence des systèmes de gestion de bases de données relationnelles entraîne également leur chute. Le SGBDR a été initialement développé pour offrir une plate-forme de stockage de données relativement simple qui accepterait de grands ensembles de données et les réduirait à une taille relativement petite. Cela a été fait au détriment de la complexité du modèle de données et de la puissance de calcul requise. À mesure que la complexité de la base de données augmentait, des procédures stockées, des vues, des fonctions et des déclencheurs ont vu le jour pour aider les administrateurs de base de données à gérer la complexité de manière cohérente et évolutive.

Malheureusement, le modèle de base de données relationnelle n'est pas orienté objet et ne correspond pas naturellement aux entités du monde réel comme le devrait un modèle de données. Cela nous amène à la nécessité d'outils intermédiaires comme les mappeurs relationnels-objets et similaires. Malheureusement, alors que ces outils ont clairement leur place dans le monde du développement d'aujourd'hui, leur utilisation vise simplement un symptôme du problème de complexité des données relationnelles, plutôt que la cause sous-jacente, qui est un désalignement du modèle de données sur le monde réel.

Cela nous amène à la deuxième partie de la question, qui était en réalité davantage une hypothèse, mais qui devrait être considérée comme une question: sommes-nous censés faire correctement notre modèle de domaine la première fois?

Oui, dans une certaine mesure. Comme la question l'a souligné, il est rarement possible de bien comprendre le problème lorsque nous commençons le processus de conception. Cependant, la différence entre un modèle de données complètement incorrect, par opposition à un modèle qui peut être modifié à mesure que nous acquérons une meilleure compréhension du domaine, est le modèle qui correspond de manière cohérente au monde réel. Cela signifie que nous devons tout mettre en œuvre pour créer un modèle de données initial qui soit cohérent avec notre compréhension du problème en termes d'entités réelles. Si nous commençons à normaliser sur les mauvaises entités, le modèle de données sera erroné de deux manières et la récupération sera difficile.

À bien des égards, le passage à des solutions de base de données «sans SQL» est le résultat des problèmes d'incohérence des modèles de données. L'utilisation d'une approche No SQL orientée objet nous amène à réfléchir davantage au mappage entre nos objets dans le code et ceux dans le monde réel - et lorsque nous rencontrons une incohérence, cela va souvent de soi car il est impossible à implémenter dans notre base de données. Cela conduit à une meilleure conception globale.

Cela conduit à la dernière question: un modèle de données relationnel est-il incompatible avec l'approche agile?

Non, mais plus de compétences sont requises. Alors que dans le monde sans SQL, il est trivial d'ajouter un champ ou de convertir une propriété en tableau, il n'est pas du tout trivial de faire ces choses dans le monde relationnel. Il faut, au minimum, quelqu'un qui est capable de comprendre à la fois le modèle de données relationnel et les entités du monde réel qu'ils représentent. Cette personne est la personne qui facilitera la mise à jour du modèle relationnel à mesure que la compréhension du modèle du monde réel change. Il n'y a pas de solution miracle pour résoudre ce problème.

theMayer
la source
1
J'espère vraiment que vous avez surdimensionné un problème de création d'un nouveau champ dans la table RDBMS pour rendre la déclaration plus dramatique. La table de base de données doit être très spéciale (ou le nouveau type de champ doit être quelque chose d'exceptionnel) pour vraiment créer un problème pour ajouter un champ.
Alexey Zimarev
Oui, mais ce n'est jamais qu'un seul domaine ...
theMayer
1
Je dirais plus souvent que ce n'est qu'un domaine. Les changements de schéma dramatiques ne sont pas si fréquents. Je ne suis pas un fan de l'utilisation de SGBDR avec une conception OO en raison d'un décalage d'impédance. Cependant, l'ajout de nouveaux types (tables) et propriétés (colonnes) est relativement facile dans les deux mondes, bien que dans NoSQL, il soit en effet un peu plus facile. Mais des changements complexes sont douloureux dans les deux cas. Pire encore, cela devient dans un système basé sur des événements avec des instantanés, contrairement à la façon dont l'expérience de développement pour un tel système est agréable.
Alexey Zimarev
Je vois que les bases de données relationnelles sont souvent utilisées comme le «marteau universel» pour résoudre les besoins de stockage de données - alors qu'en fait il y a des raisons très spécifiques de les utiliser. Dans un système soigneusement envisagé, il est rarement nécessaire de s'inquiéter des problèmes sur lesquels j'ai écrit dans ma réponse - je m'adresse à un public plus général qui n'a peut-être pas l'expérience pour arriver à une conception de système appropriée dès le départ.
theMayer
Il n'y a pas de différence entre le modèle relationnel et il correspond généralement au monde réel aussi bien qu'à tout autre type de modèle. Certaines opérations seront plus faciles avec un type et d'autres avec un autre. Le problème est lorsque vous créez un modèle d'un type (orienté objet) et essayez de l'implémenter avec des outils d'un autre type (relationnel). Ça ne marche pas bien. Mais le monde réel n'est pas orienté objet. C'est juste et vous le modélisez. Et doivent utiliser les bons outils pour le type de modèle sélectionné.
Jan Hudec
-1

Le point principal n'est pas de refactoriser tellement que votre modèle change au-delà de toute reconnaissance. Même avec un développement itératif, vous devriez vraiment vous baser sur des éléments existants et ne pas les refondre en morceaux.

Cela vous donne 2 options principales pour gérer les grands changements lorsqu'ils surviennent: la première consiste à créer la couche DB en tant qu'API, à utiliser des procédures stockées afin qu'elles puissent être modifiées pour convenir au client sans changer le schéma de données sous-jacent.

L'autre façon est de remplacer les tables par un peu de migration de données. Lorsqu'un changement à grande échelle est requis, vous créez le nouveau schéma et implémentez un ensemble de scripts pour prendre les anciennes données et les masser dans le nouveau format. Cela prend du temps à le faire, c'est pourquoi vous vous fiez davantage à des méthodes moins coûteuses pour modifier l'accès aux données (par exemple via des SP) comme premier choix.

Donc: 1. essayez de penser à l'avance avec le design afin de ne pas avoir à changer les choses.

  1. Comptez sur des wrappers ou des API pour que les modifications soient limitées ou puissent être cachées dans un composant isolé

  2. Prenez le temps de mettre à niveau correctement si vous le devez.

Ces étapes s'appliquent à tout, pas seulement aux bases de données.

gbjbaanb
la source
Le schéma sous-jacent doit parfois être modifié. Au fur et à mesure que l'application entre dans les tests clients, de nouveaux attributs surgissent dont vous n'avez jamais entendu parler, les attributs que vous pensiez être des nombres se révèlent être des chaînes, les relations que vous attendiez à 1: 1 se révèlent ne pas l'être après tout et ainsi de suite. Vous ne pouvez pas couvrir ce genre de choses derrière les procédures stockées (en outre, les procédures stockées font partie du problème, car, comme d'autres choses dans la base de données, elles ne vivent pas dans le contrôle de version).
Jan Hudec
@JanHudec depuis quand les SP ne vivent-ils pas dans le contrôle de version? Vous pouvez couvrir de telles choses, vous modifiez l'API SP pour prendre une chaîne et l'écrire dans un champ différent, en gérant les anciens numéros et les nouvelles chaînes dans un peu de code dans votre SP. Pas le plus beau, mais cela peut être mieux que d'aller sur chaque site client pour migrer leurs données vers le nouveau format de chaîne (il y a de meilleurs exemples, mais vous avez l'idée). Si le changement s'avère important, vous devez migrer, mais au moins avec une API DB, vous avez également d'autres options moins chères.
gbjbaanb
Vous devez toujours vous rendre sur chaque site client pour installer le SP et ajouter le nouveau champ. Et lorsque vous y êtes, vous pouvez également migrer les données. Les SP sont utiles dans la mesure où ils vous permettent de créer une interface rétrocompatible si plusieurs applications accèdent à la base de données, vous n'avez donc pas besoin de toutes les mettre à niveau en même temps. Mais ils n'enregistrent aucune étape lorsque le schéma doit changer en raison de l'évolution des exigences.
Jan Hudec