Eine der grössten Herausforderungen beim Design einer neuen Applikation ist wohl das Design des Domain Models, beziehungsweise der Datenbank, welche der Applikation als Fundament dient. Die Entitäten, welche hier definiert werden, kommen oft an vielen Stellen in der Applikation wieder zur Verwendung. Entsprechend gravierend sind zu spät festgestellte Fehler im Datenmodell, und entsprechend schwierig ist es, diese nachträglich noch zu korrigieren.
Deshalb möchte ich an dieser Stelle ein Element für die Modellierung des Domain Models vorstellen, welches sich so im klassischen SQL zwar nicht findet, aber dank Abstraktion dennoch möglich ist: Vererbung.
Wie schon angetönt ist Vererbung etwas, das in den klassischen Ausprägungen von SQL (z.B. MySQL oder Transact-SQL) nicht existiert. Jedes Objekt (zum Beispiel die Daten einer Person) wird im SQL als eine Zeile in einer Tabelle abgelegt. Und für alle anderen Datensätze in derselben Tabelle gilt, dass sie gleichwertige Instanzen derselben Entität (als z.B. genau gleiche generische Personen) sind. Es ist nicht möglich zusätzlich eine Entität "Angestellter" zu definieren und mit SQL auszudrücken, dass auch ein Angestellter eine Person ist - entweder eine Person oder ein Angestellter, aber nicht beides.
Im Zusammenspiel mit den zahlreichen heute verfügbaren Frameworks wird jedoch immer seltener direkt mit SQL aus einer Applikation auf einen Datenspeicher zugegriffen. Sehr weit verbreitet sind ORM - Object Relational Mapper, welche dem SQL-Datenmodell ein Domain Model aus Klassen aus der objektorientierten Programmierung entgegenstellen. Und in der objektorientierten Programmierung ist nun Vererbung eines der typischsten Elemente.
Wenn mit einem ORM gearbeitet wird, existiert das Modell für den Datenspeicher zweifach, redundant: Einmal als Datenbank und einmal als Klassenstruktur. Somit gibt es in der Regel zwei Ansätze, um diese beiden Systeme zu entwerfen: Database-First und Code-First. Ersteres bedeutet, dass zuerst die Datenbank entworfen und erstellt wird, und das ORM anhand davon die Klassen generiert. Bei Code-First hingegen werden zuerst die Klassen definiert und daraus die Datenbank. Vererbung kann nur im Code-First Ansatz angewendet werden.
Die Frage, welche nun zu klären ist, was denn passiert, wenn ein ORM mit einem Domain Model konfrontiert wird, welches Vererbung einsetzt. In der Tat ist es so, dass es mehrere Möglichkeiten gibt, eine Vererbungsstruktur nach SQL zu "übersetzen"... Um die Optionen zu illustrieren wird folgendes, einfaches Klassenmodell verwendet:
Die Klassen Bike und Car erben beide von der abstrakten Klasse Vehicle ("Fahrzeug"). Ein Fahrzeug hat als Eigenschaften eine eindeutige ID, eine Farbe und die Anzahl Räder hinterlegt. Fahrräder haben zusätzlich die Eigenschaft, wie viele Gänge vorhanden sind, während Autos über die Information verfügen, wie viele Sitzplätze existieren.
Eine erste Möglichkeit besteht darin, die gesamte Vererbungsstruktur auf eine einzige Tabelle zu kollabieren. Dabei werden die Felder, welche alle Unterklassen einführten zur Basisklasse hinzugefügt. Zusätzlich wird eine neue Spalte "Discriminator" generiert, welche dazu dient, die einzelnen Subtypen auseinanderzuhalten.
Als ERD dargestellt, sieht die damit generierte Datenbankstruktur wie folgt aus:
Table-per-Hierarchy hat folgende Vor- und Nachteile:
Vorteile:
Nachteile:
Beim Ansatz Table-per-Type wird für jede Klasse ("Type") eine eigene Tabelle erstellt. Die entstandenen Tabellen werden mittels 1:1 Referenz miteinander verknüpft. Der Primärschlüssel der Basisklasse dient als Fremdschlüssel und zugleich auch als Primärschlüssel in den erbenden Tabellen.
Vorteile:
Nachteile:
Die dritte Möglichkeit besteht darin, jede konkrete (nicht-abstrakte) Klasse als eigene Klasse zu modellieren. Dabei werden für jede konkrete Unterklasse alle Felder der Superklassen mit den eigenen Feldern zusammengezogen und daraus eine Tabelle gebildet. Die daraus resultierenden Tabellen werden im SQL nicht miteinander verknüpft.
Vorteile
Nachteile
Welcher Ansatz gewählt werden sollte hängt einerseits davon ab, ob die Diskrepanzen der einzelnen Ansätze die gewünschte Modellierung verunmöglichen (etwa wenn eine abstrakte Klasse eine andere Klasse referenzieren soll ist TPC nicht möglich), andererseits aber auch schlicht und einfach, ob das gewählte ORM den Ansatz unterstützt. Während zum Beispiel Entity Framework 6 alle drei Ansätze unterstützt kann Entity Framework Core bisher nur TPH. Hat man damit immer noch mehrere Ansätze zur Auswahl kann man nach dem persönlichen Gusto gehen, bzw. danach urteilen, welche Vor- bzw. Nachteile für einen persönlich überwiegen.