Relationale Datenbanken und iterative Entwicklung

19

Bei vielen Ansätzen zur Softwareentwicklung wie agilen Methoden, domänengesteuertem Design und objektorientierter Analyse und Design wird empfohlen, einen iterativen Ansatz für die Entwicklung zu wählen.

Wir sollten unser Domain-Modell also nicht gleich beim ersten Start des Projekts fertigstellen. Stattdessen überarbeiten wir das Modell im Laufe der Zeit, weil wir mit der Zeit ein tieferes Verständnis der Problemdomäne gewinnen.

Abgesehen davon können sich die Anforderungen ändern, auch wenn wir versuchen, ein perfektes Modell im Voraus zu bekommen, von dem ich bereits überzeugt bin, dass es sehr schwierig ist. So , nachdem die Software hat sich auf die Produktion bereitgestellt wurde, können die Endbenutzer feststellen , dass eine bestimmte Anforderung nicht vollständig verstanden wird , oder noch schlimmer, wurde einige Anforderung fehlt.

Der Punkt hier ist, dass wir möglicherweise das Modell ändern müssen, nachdem die Software bereitgestellt wurde. In diesem Fall liegt ein Problem vor: Die Produktionsdatenbank enthält wichtige Benutzerdaten und ist bereits im Format für das alte Modell angepasst .

Das Aktualisieren des Codes kann eine schwierige Aufgabe sein, wenn der Code nicht gut entworfen ist und das System groß ist. Aber es kann mit der Zeit geschehen, wir haben Tools wie Git, die uns dabei helfen, ohne die produktionsbereite Version zu beschädigen.

Wenn sich das Modell ändert, die Eigenschaften von Klassen verschwinden oder was auch immer, sollte sich auch die Datenbank ändern. Aber wir haben ein Problem: Es gibt bereits Daten, die nicht verloren gehen können und die bereits für das alte Modell formatiert sind.

Es scheint, dass eine relationale Datenbank hier ein Hindernis darstellt, das uns daran hindert, iterativ Software zu entwickeln und sogar zu aktualisieren, wenn dies von Endbenutzern gefordert wird.

Ein Ansatz, den ich bereits verwendet habe, war das Codieren einer speziellen Klasse, die alte Datenbanktabellen neuen zuordnet. Diese Klassen wählen also Daten im alten Format aus, konvertieren sie in das vom neuen Modell verwendete Format und speichern sie in den neuen Tabellen.

Dieser Ansatz scheint nicht der beste zu sein. Meine Frage lautet hier: Gibt es bekannte und empfohlene Ansätze, um die iterative Entwicklung mit relationalen Datenbanken in Einklang zu bringen?

user1620696
quelle
6
Ich denke übrigens, dass dies nichts mit relationalen Datenbanken zu tun hat . Ich habe ein ähnliches Problem mit einem Projekt, an dem ich arbeite, aber wir haben es mit dem Schema für unsere JSON-Zeichenfolgen, die sehr nicht relationale Objekte darstellen. Es betrifft wahrscheinlich alle Formen der Persistenz gleichermaßen.
Ixrec
1
Sie ändern das Datenbankschema so, dass keine Daten verloren gehen ( en.wikipedia.org/wiki/Schema_migration) .
RemcoGerlich
1
Ich bin mir sicher, dass dieses Thema irgendwo zuvor ausführlich besprochen wurde, kann es aber bei Programmierern nicht finden. Aber siehe hier martinfowler.com/articles/evodb.html oder hier stackoverflow.com/questions/334059/…
Doc Brown
1
"Abgesehen davon können sich die Anforderungen ändern, auch wenn wir versuchen, ein perfektes Modell im Voraus zu bekommen, von dem ich bereits überzeugt bin, dass es sehr schwierig ist." Ich möchte hinzufügen, dass Sie nicht einmal versuchen sollten, ein (nahezu perfektes) Modell nach vorne zu bringen. Das könnte Ihre Denkweise auf eine Art von Lösungen beschränken, anstatt Ihre Optionen offen zu halten.
Bent

Antworten:

15

Es müssen keine speziellen Klassen sein, aber ja, Sie benötigen etwas, das die Datenbank im vorherigen Format übernimmt und in die aktuelle konvertiert.

Die Sache hier ist, dass Sie einen Prozess zum Schreiben und Testen dieser Skripte und Disziplin entwickeln müssen, um die Test- und Produktionsdatenbanken nie von Hand, sondern immer von Migrationsskripten zu berühren.

Jedes Mal, wenn Sie eine Änderung an der Datenbank vornehmen müssen, schreiben Sie ein Skript, das dies in SQL oder mithilfe Ihrer ORM-Ebene ausführt, und übergeben es zusammen mit den Änderungen, die das neue Schema erfordern, an Ihre Versionskontrolle. Dann haben Sie ein Steuerungsskript, das die Datenbank aktualisiert, indem alle Migrationsskripten, die noch nicht angewendet wurden, in einer Sequenz angewendet werden.

Und stellen Sie sicher, dass Sie nur freigegebene Entwicklungs-, Test- und QA-Umgebungen ändern, indem Sie die Skripte anwenden und auf eine frühere Version zurücksetzen, wenn sie nicht funktionieren. So können Sie sicher sein, dass sie wie vorgesehen funktionieren, wenn Sie sie für die Produktion freigeben .

Die Neuinstallation erfolgt einfach durch Anwenden aller Skripte. Nach einer Weile werden Sie vielleicht Hunderte von ihnen haben und denken, dass es sehr ineffizient ist, aber tappen Sie nicht in die Falle, wenn Sie versuchen, es zu optimieren. Die Installation ist eine einmalige Aufgabe, die schnell erledigt werden kann.

@ Doc Brown hat bereits Martin Fowler verlinkt : Evolutionary Database Design und /programming/334059/agile-development-and-database-changes , und ich würde Alex Papadimoulis hinzufügen : Database Changes Done Right , was kürzer ist und hat einige Beispiele.

Als gutes Beispiel für die Implementierung eines solchen Prozesses empfehle ich Alembic . Es basiert auf dem Python SQLAlchemy- Framework, kann jedoch mit anderen Sprachen und Frameworks verwendet werden, wenn diese keine eigene Migrationsunterstützung haben. Auf der Wikipedia-Seite zur Schema-Migration sind weitere solche Tools aufgeführt .

Jan Hudec
quelle
1
@Tibo Sie erstellen das Schema von Grund auf neu, indem Sie dieselbe Sequenz von Skripten ausführen. So meistern Sie das Problem. Vorausgesetzt, dass Sie als Standard von jeder Instanz der Datenbank - einschließlich einer, die noch nicht existiert - zu einem aktuellen Schema gelangen und darauf vertrauen können, dass es dasselbe ist. Es gibt keine Notwendigkeit, zwei Möglichkeiten gemäß Ihrem Beispiel zu haben. (Wenigstens keine konsistente Grundlinie angegeben - der erste Schritt besteht darin, die Grundlinie festzulegen, und sobald Sie diese Grundlinie erreicht haben, verschwindet das Problem.)
Murph
1
Daumen hoch für Alex 'Artikel; es mag nicht kürzer sein, aber es macht eine viel praxisorientiertere und unterhaltsamere Lektüre.
Murphy
1
Wir sind ein Agile-Shop und betreiben einen 100% igen Verfügbarkeitsservice. Beides gilt auch für die DB. Wir migrieren das Produktionsschema im Durchschnitt einmal am Tag und ich würde alles, was Jan gesagt hat, unterstützen. Eine weitere wertvolle Maßnahme ist das sogenannte Migrationstesten, das im Rahmen unseres Erstellungs- und Bereitstellungsprozesses ausgeführt wird. Es erstellt einen Schema-Snapshot aus der Produktion, wendet alle ausstehenden Migrationen vom Master darauf an und führt dann die Komponententests des aktuell bereitgestellten Produktionscodes für dieses Schema aus. Das Ziel besteht darin, zu überprüfen, ob durch das Anwenden der Migrationen das laufende System nicht beschädigt wird.
Gordon Wrigley
1

Seltsamerweise ist dies genau das Problem, mit dem mein aktuelles Entwicklungsteam konfrontiert ist. Die Frage enthält mehrere Unterfragen, die unabhängig voneinander beantwortet werden.

Beschränkt eine relationale Datenbank in erster Linie das Datenmodell zu sehr, was Änderungen sehr schwierig macht?

Mit Sicherheit , aber nicht unbedingt aus den genannten Gründen. Leider führt die Vielseitigkeit relationaler Datenbankverwaltungssysteme auch zu deren Untergang. Das RDBMS wurde ursprünglich entwickelt, um eine relativ einfache Datenspeicherplattform anzubieten, die große Datenmengen akzeptiert und auf eine relativ kleine Größe reduziert. Dies erfolgte auf Kosten der Komplexität des Datenmodells und der erforderlichen Rechenleistung. Mit zunehmender Komplexität der Datenbank entstanden gespeicherte Prozeduren, Ansichten, Funktionen und Trigger, mit denen Datenbankadministratoren konsistent und skalierbar mit der Komplexität umgehen können.

Leider ist das relationale Datenbankmodell nicht objektorientiert und kann nicht wie ein Datenmodell auf reale Entitäten abgebildet werden. Das führt uns zu der Notwendigkeit von Mittelsmännern wie objektrelationalen Mappern und dergleichen. Obwohl diese Tools in der heutigen Entwicklungswelt eindeutig einen Platz haben, zielt ihre Verwendung nur auf ein Symptom des Problems der relationalen Datenkomplexität ab und nicht auf die zugrunde liegende Ursache, die eine Fehlausrichtung des Datenmodells zur realen Welt darstellt.

Das führt zum zweiten Teil der Frage, der eigentlich eher eine Annahme war, aber als Frage zu verstehen ist: Sollen wir unser Domain-Modell gleich beim ersten Mal richtig machen?

Ja, bis zu einem gewissen Grad. Wie die Frage zeigte, ist es selten möglich, das Problem vollständig zu verstehen, wenn wir mit dem Entwurfsprozess beginnen. Der Unterschied zwischen einem vollständig inkorrekten Datenmodell und einem Modell, das möglicherweise angepasst wird, wenn wir ein besseres Verständnis der Domäne erlangen, ist jedoch das Modell, das die reale Welt kohärent abbildet. Dies bedeutet, dass wir alle Anstrengungen unternehmen müssen, um ein erstes Datenmodell zu erstellen, das mit unserem Verständnis des Problems in Bezug auf seine realen Entitäten übereinstimmt. Wenn wir beginnen, uns auf die falschen Entitäten zu normalisieren, ist das Datenmodell in zweierlei Hinsicht falsch und die Wiederherstellung wird schwierig.

In vielerlei Hinsicht ist die Umstellung auf "No SQL" -Datenbanklösungen auf die Probleme der Datenmodellinkohärenz zurückzuführen. Verwenden eines objektorientierten No SQL-Ansatzes veranlasst uns, mehr über die Zuordnung zwischen unseren Objekten im Code und denen in der realen Welt nachzudenken Datenbank. Dies führt zu einem besseren Gesamtdesign.

Das führt zur letzten Frage: Stimmt ein relationales Datenmodell nicht mit dem agilen Ansatz überein?

Nein, aber mehr Geschick ist erforderlich. Während es in der No-SQL-Welt trivial ist, ein Feld hinzuzufügen oder eine Eigenschaft in ein Array zu konvertieren, ist es in der relationalen Welt überhaupt nicht trivial, diese Dinge zu tun. Zumindest braucht es jemanden, der in der Lage ist, sowohl das relationale Datenmodell als auch die realen Entitäten, die sie darstellen, zu verstehen. Diese Person ist die Person, die die Aktualisierung des relationalen Modells erleichtert, wenn sich das Verständnis des realen Modells ändert. Es gibt kein Patentrezept, um dieses Problem zu lösen.

theMayer
quelle
1
Ich hoffe wirklich, dass Sie ein Problem beim Erstellen eines neuen Felds in der RDBMS-Tabelle übergroß haben, um die Aussage dramatischer zu gestalten. Die Datenbanktabelle muss sehr speziell sein (oder der neue Feldtyp muss etwas Besonderes sein), damit beim Hinzufügen eines Felds wirklich ein Problem auftritt.
Alexey Zimarev
Ja, aber es ist nie nur ein Feld ...
theMayer
1
Ich würde öfter sagen, es ist nur ein Feld. Dramatische Schemaänderungen sind selten. Ich bin kein Fan von RDBMS mit OO-Design aufgrund von Impedanzfehlanpassungen. Das Hinzufügen neuer Typen (Tabellen) und Eigenschaften (Spalten) ist jedoch in beiden Welten relativ einfach, obwohl es in NoSQL tatsächlich etwas einfacher ist. Komplexe Veränderungen sind jedoch in beiden Fällen schmerzhaft. Noch schlimmer wird es im ereignisbasierten System mit Snapshots, im Gegensatz dazu, wie angenehm die Entwicklungserfahrung für ein solches System ist.
Alexey Zimarev
Ich sehe, dass relationale Datenbanken häufig als "Universalhammer" zur Lösung von Datenspeicheranforderungen verwendet werden - obwohl es tatsächlich sehr spezielle Gründe gibt, sie zu verwenden. In einem sorgfältig durchdachten System muss man sich selten Gedanken über die Themen machen, über die ich in meiner Antwort geschrieben habe. Ich spreche ein allgemeineres Publikum an, das möglicherweise nicht die Erfahrung hat, im Vorfeld zu einem geeigneten Systemdesign zu gelangen.
theMayer
Es gibt keine Diskrepanz zwischen relationalem Modell und es wird normalerweise genauso gut auf die reale Welt abgebildet wie auf jede andere Art von Modell. Einige Operationen werden bei der einen Art und bei der anderen Art einfacher sein. Das Problem ist, wenn Sie ein Modell einer Art (objektorientiert) erstellen und versuchen, es mit Werkzeugen einer anderen Art (relational) zu implementieren. Das funktioniert nicht gut Die reale Welt ist jedoch nicht objektorientiert. Es ist einfach so und du modellierst es. Und müssen die richtigen Werkzeuge für die ausgewählte Art von Modell verwenden.
Jan Hudec
-1

Der wichtigste Punkt ist, nicht so stark umzugestalten, dass sich Ihr Modell bis zur Unkenntlichkeit ändert. Selbst bei iterativer Entwicklung sollten Sie wirklich auf vorhandenen Dingen aufbauen und sie nicht in Stücke überarbeiten.

Auf diese Weise haben Sie zwei Hauptoptionen, um große Änderungen zu bewältigen: Die erste besteht darin, die DB-Ebene als API zu erstellen und gespeicherte Prozeduren zu verwenden, damit sie an den Client angepasst werden können, ohne das zugrunde liegende Datenschema zu ändern.

Die andere Möglichkeit besteht darin, Tabellen durch ein wenig Datenmigration zu ersetzen. Wenn eine umfangreiche Änderung erforderlich ist, erstellen Sie das neue Schema und implementieren eine Reihe von Skripten, um die alten Daten in das neue Format umzuwandeln. Dies ist zeitaufwändig, weshalb Sie sich in erster Linie auf günstigere Methoden zur Änderung des Datenzugriffs (z. B. über SPs) verlassen.

Also: 1. Versuchen Sie, mit dem Design vorauszudenken, damit Sie nichts ändern müssen.

  1. Verlassen Sie sich auf Wrapper oder APIs, um Änderungen zu vermeiden oder sie in einer isolierten Komponente zu verbergen

  2. Nehmen Sie sich die Zeit für ein korrektes Upgrade.

Diese Schritte gelten für alles, nicht nur für Datenbanken.

gbjbaanb
quelle
Das zugrunde liegende Schema manchmal muss geändert werden. Wenn die Anwendung in Kundentests eintritt, tauchen neue Attribute auf, von denen Sie noch nie gehört haben. Attribute, von denen Sie dachten, dass sie Zahlen sind, entpuppen sich als Zeichenfolgen. Relationen, von denen Sie erwartet hatten, dass sie 1: 1 sind, entpuppen sich schließlich als nicht so und so weiter. Sie können diese Art von Dingen nicht hinter gespeicherten Prozeduren verbergen (außerdem sind gespeicherte Prozeduren Teil des Problems, da sie wie andere Dinge in der Datenbank nicht in der Versionskontrolle leben).
Jan Hudec
@JanHudec seit wann leben SPs nicht in der Versionskontrolle? Sie können sich mit solchen Dingen befassen, indem Sie die SP-API so ändern, dass sie einen String aufnimmt und in ein anderes Feld schreibt. Dabei werden die alten Zahlen und neuen Strings in einem Stück Code in Ihrem SP behandelt. Nicht das Schönste, aber es kann besser sein, zu jedem Kundenstandort zu gehen, um seine Daten in das neue Zeichenfolgenformat zu migrieren (es gibt bessere Beispiele, aber Sie haben die Idee). Wenn sich herausstellt, dass die Änderung sehr umfangreich ist, müssen Sie migrieren, aber zumindest mit einer DB-API haben Sie auch andere, billigere Optionen.
Gbjbaanb
Sie müssen immer noch zu jedem Kundenstandort gehen, um den SP zu installieren und das neue Feld hinzuzufügen. Und wenn Sie dort sind, können Sie auch die Daten migrieren. SPs sind nützlich, da Sie damit eine abwärtskompatible Schnittstelle erstellen können, wenn mehrere Anwendungen auf die Datenbank zugreifen, sodass Sie nicht alle gleichzeitig aktualisieren müssen. Sie speichern jedoch keine Schritte, wenn sich das Schema aufgrund sich ändernder Anforderungen ändern muss.
Jan Hudec