Der nachfolgende Text wurden mit KI erstellt und kann Fehler enthalten. Fehler gefunden? Bei GitHub editieren
Folge 299 - Wie Datenbanken die Architektur formen
So, dann herzlich willkommen zu einer weiteren Episode von Software-Architektur im Stream.
Heute geht es um das Thema, wie Datenbanken die Architektur formen.
Kurz zur Motivation.
Es gibt so mehrere Motivationen.
Die eine Sache ist, ich habe letzte Woche in einem Kontext so eine Frage beantwortet zum Thema, ich habe einen Repository für eine Tabelle.
Ich wähle eine Anfrage über mehrere Tabellen, wie bekomme ich das in mein System?
Und diese Frage ist tatsächlich so, dass ich das Gefühl habe, dass man sie nicht in zwei Sätzen beantworten kann, weil da steht was über einen Repository, da steht was über Tabellen und ich glaube, ein Repository für eine Tabelle macht eigentlich keinen Sinn.
Darüber reden wir aber gleich noch.
Dann geht es um das Thema Relationale Datenbanksysteme und Dokumentendatenbanken, die ja unterschiedliche Ansätze haben und da ist die Frage, wie das sich auf Architektur niederschlägt.
Und dann ist noch ein Thema die Geschichte mit den UR-Mappern, JPA, die im Java-Universum Standard sind.
Active Record beispielsweise ist eher bei Ruby und Rails Standard.
Und das ist die Frage, was jetzt von den beiden Ansätzen eigentlich besser ist.
Der grobe Fahrplan ist halt, dass ich erst mal anfangen und etwas sage über das Domänenproblem.
Also wie wollen wir eigentlich aus einer objektorientierten Sicht unsere Domäne ausdrücken?
Da ist halt Domain Driven Design das bekannt sinnvolle Werkzeug für.
Und dann werden wir anschließend darüber reden, wie denn so Relationale Datenbanken funktionieren.
Dann geht es darum, wie man das aufeinander gemeldet bekommt.
Und dann werde ich glaube ich noch was sagen zum Thema, wie alternative Modelle aussehen.
Wie ich also nicht nur mit DDD auf Relationale Datenbanken zugreifen kann, sondern auch mit anderen Modellen.
Und dann geht es noch um das Thema mit den Dokumentendatenbanken.
Genau, soweit sozusagen der Fahrplan.
Und dann können wir loslegen.
Ich habe tatsächlich so ein bisschen Folien vorbereitet.
Das sind Recycled Folien.
Das heißt, das ist jetzt nichts, was ich extra hierfür angelegt habe.
Wir hatten ja auch schon eine Folge zu Domain Driven Design als ein durchgängiges Beispiel.
Und ich glaube, da kommen die her.
Und wir fangen also an mit dem Thema, was denn eigentlich so taktisches Domain Driven Design sagt.
Und das sagt eben erstmal, dass es halt Entities gibt.
Entities sind Sachen, die ich halt identifizieren kann durch eine Identität, also eine Person.
Auch eine Person, die halt genauso heißt wie ich, die genauso alt ist wie ich, ist eben nicht dieselbe Person.
Also eine Entität ist ein Objekt oder eine Entity ist ein Objekt, was ich durch eine Identität identifizieren kann.
Also eine Person, die genau dieselben Attribute hat wie ich, bin trotzdem nicht ich.
Das heißt, das sind zwei unterschiedliche Personen.
Und im Gegensatz dazu gibt es dann das Value Object, das halt einfach einen Wert repräsentiert.
Also sowas wie zwei Euro, zwei Meter oder so etwas.
Und ich sollte vielleicht sagen, grundsätzlich ist es eben so, dass ich bei Domain Driven Design diesen Objekten eine Logik gebe.
Das heißt also die zwei Meter, die zwei Euro.
Vielleicht kann ich die zwei Euro in irgendwas umrechnen oder sonst irgendwie etwas damit machen.
Das ist eben ein objektorientiertes System und hat deswegen objektorientierte Logik.
So und dann gibt es darauf aufbauend die Aggregates.
Und Aggregates sind halt irgendwelche Dinge, die mehrere Entities oder Value Objects umfassen.
Und ich habe mir jetzt für diese Präsentation rausgesucht, einmal ein Customer.
Das ist also ein Kunde und ich würde halt behaupten, ein Kunde hat zum Beispiel Basisinformationen.
Das ist vielleicht eine Entity und er hat irgendwie Adressen.
Die sind vielleicht dann auch Entities.
Das heißt also insgesamt habe ich ein Aggregate, den Kunden und seine Adressen.
Das sind zwei verschiedene Entities.
Und das Aggregate sorgt jetzt dafür, dass diese Sachen konsistent sind.
Das heißt, wenn ich eine Änderung mache in diesem Aggregate, wird dafür gesorgt, dass das gesamte Aggregate konsistent ist.
Ich zeige gleich nochmal das Beispiel.
So und da ist das Aggregate Root für zuständig.
Das heißt also, das Aggregate Root sorgt jetzt dafür, dass dieses Objektwirrwarr irgendwie konsistent ist.
Und da sind Entities und Value Objects drin.
So dann gibt es das Repository und das gibt mir die Illusion einer Kollektion von Aggregates.
So und ich hatte ja gesagt, da gab es diese Aussage mit, ich habe ein Repository für eine Tabelle.
Nein, ein Repository im Sinne von Domain Driven Design ist ein Aggregate, ist für Aggregates da.
Also für sowas wie einen Kunden.
Und das wird wahrscheinlich mehr als eine Tabelle sein.
Da kann man jetzt irgendwie sagen, naja, cool, das ist halt Haarspalterei.
Wir werden später dazu kommen, dass ich glaube, dass das irgendwie nicht Haarspalterei ist, sondern dass wir hier eigentlich von einem objektorientierten Konzept losgehen.
Also ich habe bis jetzt nicht gesagt, dass wir eine Datenbank haben.
Wir haben nur die Illusion, dass wir eine Kollektion von Aggregates haben.
Die können wir theoretisch in den Speicher tun und mit irgendeiner Persistenz vielleicht auch persistieren.
Aber da steht jetzt nichts über relationale Datenbanken.
Das heißt, da steht nichts über Tabellen.
Wir machen also ein reines objektorientiertes Konstrukt, wäre meine Interpretation zumindest von Domain Driven Design.
So und dann gibt es die Factories, die halt solche komplizierten Value Objects oder Aggregates zusammenbauen können.
Und dann habe ich darüber die Services, die jetzt die Logik enthalten, die halt nicht in Entities oder Aggregates untergebracht werden kann.
Logischerweise ist es so, dass wenn ich jetzt versuche, zwei Aggregates gleichzeitig zu modifizieren, dann kann ich das nicht in einem Aggregate unterbringen.
Dann werde ich das wohl in einer Entity machen.
Also es wird vermutlich irgendwelche Services geben, die jetzt zum Beispiel einen Kunden und eine Bestellung anfassen.
Weil man gibt einem Kunden halt eine Bestellung auf.
Und ich habe das hier jetzt irgendwie mal mit das Beispiel nochmal ausgeführt.
Also ich habe eine Bestellung, das ist eine Aggregate, die hat Bestellpositionen, das sind einzelne Entities.
Dann habe ich einen Customer, Customer ist ein Aggregate, der hat eine oder viele Adressen, das sind Value Objects.
Und jetzt müsste ich halt über das Aggregate Route gehen, um zum Beispiel eine Order Position in das Aggregate reinzutun.
Und vielleicht muss ich da dann irgendwelche Bedingungen haben.
Also vielleicht muss ich halt irgendwie dafür sorgen, dass der Gesamtwert der Bestellung dann auch geändert wird.
Das kann ich durch dieses Aggregate Route auf die Reihe bekommen.
Das heißt, das Aggregate Route sagt jetzt, ich packe eine Bestellposition rein.
Und außerdem sorge ich dafür, dass der Gesamtwert der Bestellung entsprechend modifiziert wird.
Und dadurch bekomme ich jetzt eben, oder das ist der Sinn von Aggregates, halt ein Konglomerat an Entities, Value Objects oder so konsistent zu halten.
Also es kann nicht passieren, dass jetzt eine Order Position da rein kommt und plötzlich der Wert der Bestellung sich nicht ändert.
Sondern diese beiden Sachen sind garantiert konsistent durch das, was im Aggregate Route passiert.
Dann haben wir eine Order Factory, die jetzt irgendwie Orders herstellen kann.
Dann haben wir eine Order Repository.
Das ist jetzt das, wo ich irgendwie hingehe und sage, gib mir halt irgendeine Bestellung.
Hinweis vielleicht dabei noch, da sollten halt fachlich sinnvolle Anfragen sein.
Also wenn ich jetzt alle Bestellungen aus der Region Nord haben möchte, dann sollte ich dort irgendwie eine Methode haben, die das halt ermöglicht.
Wenn ich sowas nicht haben möchte, sollte ich die Methode auch nicht haben.
Das heißt, insbesondere solche ganz supergenerischen Anfragen sind nicht das, was ich hier umsetzen möchte.
Also ich möchte keine generischen Anfragen umsetzen, die jetzt beliebige Sachen zurückgeben können.
Das ist für mich auch eine wichtige Sache, weil das eben zum Beispiel in Abrede stellt, dass solche supergenerischen Schnittstellen irgendeinen Sinn machen.
GraphQL ist ein Beispiel, was dann eben wohl wenig Sinn macht.
Da oben habe ich jetzt den Order Service.
Das ist also der Prozess, der jetzt dafür sorgt, dass Bestellungen gemacht werden können.
Und dieser Service ist jetzt etwas, was eben Customer und Order Aggregates anfasst.
Deswegen ist es eben ein Service und ich kann das nicht einem von beiden zuschlagen.
Was hier noch interessant ist, ist, dass so wie ich es jetzt modelliert habe, Customer und Order keine Beziehung haben.
Offensichtlich sollte ich Bestellungen für einen Kunden haben.
Da wird es eine Beziehung geben.
Ich habe die jetzt nicht eingemalt, weil das etwas anderes ist.
Also der Kunde besitzt eine Adresse.
Das ist eine Komposition.
Die Bestellung besitzt eine Order Position.
Das ist auch eine Komposition.
Dazwischen ist eine Bestellung.
Da kann ich zum Beispiel nicht unbedingt Konsistenzen erzwingen.
Das bedeutet, dass es zwei getrennte Aggregates sind.
Dazwischen gibt es eine Beziehung.
Die kann ich jetzt zum Beispiel abbilden, indem ich in einer Bestellung die ID eines Kunden speichere.
Dann kann ich zum Repository gehen von den Kunden und die Kunden rausholen.
Ich könnte auch so etwas machen wie Lazy Loading.
Mit dem OR-Mapper biete ich die Möglichkeit, Lazy etwas nachzuladen.
Aber es ist eine andere Beziehung, weil das nicht durch das Aggregate Route gemanagt wird.
Ich habe mich entschieden, diese Beziehung gar nicht einzumalen.
Das heißt, das wird indirekt durch irgendwelche IDs gemacht.
Ich will das gar nicht so detailliert diskutieren.
Ich habe eine Episode gemacht, in der ich über taktische Instrumente und Design geredet habe.
Die zehn Minuten, die ich hier verbraucht habe, sind nur ein Überflug.
Einfach deswegen, weil ich ja eigentlich ein anderes Thema habe.
Dazu muss ich mir nochmal meine Notizen zusammensammeln.
Wogegen wir das jetzt halten müssen, sind Relationale Datenbanksysteme.
Relationale Datenbanksysteme sind immer noch die dominierende Technologie.
Trotz der ganzen No-Secret-Grafik.
Jetzt ist die Frage, wie in einer relationalen Datenbank Daten modelliert werden.
Da gibt es insbesondere diese Idee von Normalisierung.
Darüber kann man auch länger reden.
Das will ich eigentlich gar nicht.
Ich habe mir jetzt hier ein sehr triviales Modell ausgesucht.
Das Modell entspricht dem, was wir vorhin bei den Aggregates diskutiert haben.
Da haben wir einmal die Bestellung.
Also eine Tabelle Bestellung.
Da wäre jetzt ein Primärschlüssel drin.
Also irgendwas, wodurch ich eine Zeile in dieser Tabelle identifizieren kann.
Irgendwelche Attribute.
Vielleicht der Wert einer Bestellung.
Keine Ahnung.
Und dann sowas wie die Bestellposition.
Der Wert einer Bestellung wäre übrigens vielleicht schon etwas, was gegen so eine Normalisierung verstößt.
Weil ich dann Daten eigentlich redundant habe.
Ich habe eben den Gesamtwert der Bestellung.
Und die Werte der einzelnen Orderpositionen, aus der ich den Gesamtwert der Bestellung berechnen kann.
Das wäre das vermutlich.
Etwas, wo ich schon irgendwie anders vorgehen würde.
Und eben nicht diesen Gesamtwert tatsächlich abspeichern würde.
Beziehungsweise ich darf das nicht, wenn es halt echt normalisiert, vollständig normalisiert sein soll.
Das ist jetzt nicht so viel anders, als das, was ich vorhin aus einer objektorientierten Sicht gezeigt habe.
Ich habe dann den Kunden.
Und der Kunde, der hat jetzt noch eine Adresse.
Eine oder mehrere Adressen.
Und dann habe ich hier so einen Kundentyp noch dazu erfunden.
Und dieser Kundentyp, so ist meine Idee.
Der sagt mir jetzt, ob das ein Großkunde ist oder ein kleiner Kunde oder was auch immer.
Und das ist jetzt eine Tabelle, wo ich nur eine ID habe.
Also ID 1 ist mein Großkunde.
ID 2 ist halt irgendwie kleinere Kunden.
Und ich hätte dann halt bei dem Kunden eine entsprechende Beziehung zu diesem Kundentyp.
Vielleicht ist in dem Kundentyp noch sowas wie eine Rabattstufe oder so.
Da sind also vielleicht irgendwie auch noch Daten drin.
Und das mache ich jetzt deswegen, weil ich dann zum Beispiel neue Kundentypen dort erfinden kann.
Und das ist etwas, was Normalisierung sozusagen erzwingt.
Und das ist jetzt ein Unterschied zu dem objektorientierten Modell.
Da hätte ich halt so einen Kundentyp wahrscheinlich irgendwie als E-Namen gebaut.
Oder irgendwie als, weiß ich nicht, vielleicht eine einfache Klasse oder so.
Vielleicht auch ein Value Object.
Worauf ich halt eigentlich hinaus möchte, ist, diese Relationen, die wir hier haben, sind nicht unbedingt deckungsgleich mit den Klassen, die wir halt in dem objektorientierten Konzept haben.
Und das zeigt sich eben auch noch insbesondere dadurch, dass wir hier zwischen Kunde und Bestellung eine Beziehung haben.
Und das ist eben auch eine Relation.
Und diese Relation ist ununterscheidbar von den anderen Relationen, die ja so eine Komposition darstellen.
Das heißt, ich habe hier Beziehungen wie zwischen Kunden und Bestellung, wo ich im Prinzip sage, ein Kunde und eine Bestellung, die sind relativ selbstständig.
Und dann habe ich Dinge, die einen Kunden genauer spezifizieren, wie ein Adress oder ein Kundentyp.
Das ist im objektorientierten Modell jeweils Teil eines Aggregates.
Hier sind diese Beziehungen, die Teil eines Aggregates wären oder diese andere Beziehung, die gar nicht so richtig modelliert ist, die sind hier gleich.
Das sind halt einfach relationale Beziehungen, wo ich dann eben über die entsprechenden Schlüssel, also ich habe dann dort zum Beispiel in der Bestellung die Kunden-ID abgespeichert und die Kunden-ID ist eben ein Primärschlüssel, mit dem ich auf die Kundentabelle zugreifen kann und mir anhand der Kundentabelle einen Kunden identifizieren kann.
Das ist der Grund, warum relationale Datenbanken nur funktionieren mit Transaktionen.
Jede fachliche Änderung, die ich mache und sei es eben nur ein Kunde, die wird mehrere Tabellen umfassen, weil eben ein Kunde mehrere Tabellen umfasst und das bedeutet, ich muss das atomar ändern können und ich muss damit jetzt auch Dinge zwischen mehreren Tabellen, kann ich jetzt eben auch atomar ändern.
Also ich kann zum Beispiel dafür sorgen, dass halt Order und Customer gemeinsam geändert werden.
Das heißt also, ich kann Transaktionen machen, die halbe Datenbank irgendwie einmal umwühlen.
Ich muss aber diese Transaktionen auf jeden Fall haben, um eben solche einzelnen fachlichen Objekte wie Customer tatsächlich fachlich sinnvoll ändern zu können.
Hier ist ein Unterschied zu dem, was Domain-Driven-Design sagt.
Domain-Driven-Design sagt halt, diese Transaktionen sind auf Ebene eines Aggregates und nur auf Ebene eines Aggregates, zwischen Aggregates, kann ich Konsistenzen nicht erzwingen.
Das ist also ein etwas eingeschränktes Modell.
Warum ist das jetzt toll?
Was ich hiermit jetzt erreichen kann, ist, ich kann eine Anfrage machen, die es mir halt erlaubt, zu schauen, wie viel Umsatz ich mit irgendeinem Kunden gemacht habe.
Das heißt, ich nehme halt und zwar tatsächlich als Zahl.
Also ich nehme mal an, hier in der Order-Position ist der Wert.
Jetzt kann ich eine Query machen, die sagt, ich selektiere hier alle Bestellungen zu irgendeinem Kunden.
Dafür selektiere ich alle Order-Positionen.
Dann summiere ich hier die ganzen Sachen auf und kriege raus irgendeine Summe.
Ich kann jetzt sogar sagen, wenn ich alle Kunden aus Hamburg haben möchte und deren Umsatz haben möchte, das kriege ich halt auch hin, dann mache ich hier ein Select auf die Adressen.
Suche mir die Adressen raus, die in Hamburg sind.
Suche mir dazu die Kunden raus, indem ich das joine.
Das kann ich also mit in einer Query schreiben.
Suche mir halt die Bestellungen mit raus, das kann ich auch mit joinen.
Suche mir dazu die Order-Position, summiere das alles auf und kriege dann halt ein Ergebnis.
Und das ist halt sehr flexibel, weil diese Query kann ich bauen, ohne dass ich vorher darüber nachgedacht habe.
Also ich muss nicht ein Requirement haben, was halt sagt, ich möchte irgendwann mal über Orte Umsätze ermitteln, sondern diese Query kann ich halt jederzeit hinschreiben.
Und ich kann sie auch noch optimieren, weil ich halt Indices einbauen kann, die halt dafür sorgen, dass diese Queries möglichst schnell sind.
Und das bedeutet, dass ich dort eben ein hohes Maß an Flexibilität habe.
Und sozusagen eine Seitennotiz.
Das Relationale Modell ist nicht sozusagen das ursprüngliche Modell von Datenbanken, sondern es gab irgendwie vorher schon andere Ideen.
Es gab halt Ideen wie zum Beispiel hierarchische Datenbanken oder so.
Ich glaube, dass die relationalen, oder es kann sehr gut sein, dass die relationalen Datenbanken durch diese höhere Flexibilität, dass das deren Vorteil im Wesentlichen ist.
Dafür ist halt Verteilung schwieriger.
Ich muss dann eben tatsächlich die gesamten Daten alle zur Verfügung haben, um eben irgendwelche Queries machen zu können.
Ich kann also jetzt nicht easy sagen, hey, die Bestellungen liegen irgendwo anders, sondern also auf einem anderen Rechner, irgendwo im Netzwerk oder bestimmte Kunden liegen woanders, weil ich dann eben an der Stelle, wo ich eine Query mache, plötzlich ganz viele Hosts habe, mit denen ich reden muss.
Und das wird irgendwie unangenehm.
Also dazu, dass solche relationalen Datenbanken im Netzwerk schwer skalierbar sind.
Es gibt sowas wie Oracle REC.
Die machen das, indem sie ein zentrales Storage-System haben, das sich alle teilen.
Das heißt, wenn dieses Storage-System am Ende ist, ist man in der Skalierung am Ende.
Und MySQL beispielsweise bietet so eine Replikation, was bedeutet, dass ich mehrere Follower habe, aber ich habe nur einen, der schreibt.
Ich kann nicht schreiben, skalieren, sondern ich kann nur lesen, skalieren, weil ich Replikas habe, von denen ich lesen kann.
Aber ich kann eben nicht jetzt noch mehr Sachen haben, wo ich schreiben kann.
Und das ist so ein bisschen der Grund, weswegen man so NoSQL und Dokument-orientierte Datenbanken bauen will.
Und das wiederum führt zu der Frage, ob die nicht irgendwie problematisch sind an einer Stelle, aber dazu kommen wir gleich.
Und wir wollen ja jetzt der Frage nachgehen, was also dieses objektorientierte Modell mit diesem relationalen Modell sozusagen zu tun hat.
Und ich würde jetzt erst mal sagen, sieht man hier ja auch in dem Beispiel, dass eben Aggregates fachlich komplexe Objekte sind, die mehrere Tabellen umfassen.
Also meine Bestellung, die ich als Aggregate formuliert habe, die hatte irgendwie diese Bestellungstabelle und die Order-Position-Tabelle.
Der Kunde, den ich als Aggregate formuliert habe, hat diese Customer-Type-Address und Customer-Tabelle und so weiter und so weiter.
Dann kann ich halt ein OR-Mapper benutzen, um halt dafür zu sorgen, als Stück Software, dass ich das halt irgendwie gemappt bekomme.
Die haben genau die Aufgabe, solche Objekte halt irgendwie zu mappen.
Und da kann ich jetzt, habe ich so Synergien.
Ich hatte schon gesagt, wenn ich jetzt einen Kunden habe und halt ich sage, ich habe einen Kunden als Aggregate, dann ist eben aus der DDD-Sicht klar, dass die Beziehung zu einer Bestellung sozusagen keine sehr intensive Beziehung ist.
Das heißt also, ich könnte dort zum Beispiel die Order entsprechend lazy nachladen.
Solche OR-Mapper bieten mir auch die Möglichkeit an, einfach ein Aggregate oder irgendetwas zu ändern und dann speichern die das halt automatisch ab, wie auch immer das halt gerade geändert worden ist.
Das heißt, ich muss mir auch nicht um sowas wie Inserts oder Updates kümmern.
Das Ganze bricht meiner Ansicht nach an der Stelle, wo ich jetzt sagen will, ich möchte halt gerne den Umsatz aller Kunden aus Hamburg bekommen.
Das kann ich relational gut lösen.
Wenn ich das versuche, objektorientiert zu lösen, mache ich halt irgendwelche Schleifen.
Und das ist halt schlimm, weil das führt dazu, dass ich eben ganz viel von der Datenbank lese und ganz viel mit der Datenbank mache, was ich eigentlich nicht muss.
Und insbesondere diese Optimierung, die die haben in die Cs und was da halt alles rumfliegt, die ignoriere ich und nutze ich nicht.
Und JPA beispielsweise aus dem Java-Universum bietet JPQL, also eine Anfragesprache, die mir erlaubt, dann sozusagen auf objektorientierter Sicht mit den Attributnamen und so weiter eine Query zu schreiben, die dann entsprechend relational umgesetzt wird.
Und ich würde behaupten, dass da dieses rein objektorientierte Konzept halt durchbrochen wird aus Performancegründen, weil ich eben diese JPQL Query sehr gut auf einer relationalen Datenbank umsetzen kann und dadurch eben mir das spare, dass ich jetzt alle Kunden raushole, schaue, ob die Kunden halt aus Hamburg kommen und dann irgendwie durch die Order-Position durchiterriere.
Das ist halt mit der relationalen Datenbank viel, viel schneller machbar, die halt durch einen Query-Optimizer die Daten gar nicht erst liest und halt irgendwelche geschickten Sachen macht.
Entities sind nicht unbedingt in der Tabelle, sagte ich schon.
Customer-Type beispielsweise ist zwar eine Tabelle, wird aber keine Entity.
Da gibt es also so eine leichte sozusagen Disruption.
Und OER-Map haben dann halt eine Vielzahl an unterschiedlichen Strategien.
Also es wird zum Beispiel dann interessant, wenn ich jetzt sage, es gibt irgendwie unterschiedliche Arten von Kunden.
Ich hatte ja tatsächlich gesagt, dass es eben Kundentypen gibt.
Wenn es zum Beispiel eine Klasse gibt, Großkunde, dann will ich ja vielleicht aus den Automatisten, will ich irgendwie automatisch, wenn ich Kunden lade, einen Großkunden bekommen, wenn halt bestimmte Konditionen da sind.
OER-Mapper können sowas konzeptionell, aber da wird es eben dann irgendwann möglicherweise schwierig.
Wir haben ein objektorientiertes Modell, also wir haben Objekte mit Logik, möglicherweise mit Vererbung, vielleicht auch mit Delegation.
Das wird irgendwann ein Problem, wenn ich halt jetzt tatsächlich aus einer rein objektorientierten Sicht komme und dann eben, also das ist ja das, was ich gerade sozusagen als Entwurfstrategie so ein bisschen thematisiere.
Ich hätte halt gesagt, Aggregates sind etwas, was fachlich motiviert ist.
Repositories sind Sachen, die mir diese Illusion dieser großen Kollektion von Objekten geben, was halt im Prinzip sagt, wenn ich so ganz orthodox, übertrieben orthodox bin, dann weiß ich gar nicht, dass die relationale Datenbank da ist.
Ich kann sogar mit Hilfe eines OER-Mappers aus so einem objektorientierten Modell eine Datenbank eben erzeugen lassen.
Das kann möglicherweise zu Problemen führen, insbesondere in Bezug auf Performance, wenn ich eben tatsächlich, irgendwann muss ich das vielleicht irgendwie durchbrechen.
Und wenn ich da halt kompliziert objektorientierte Modelle baue, kriege ich vielleicht ein relationales Modell, was dann irgendwie gar nicht schön ist.
Und es gibt halt bestimmte Dinge, wo ich eigentlich mit diesem Ansatz nicht, also das ist etwas, wo man jetzt diskutieren kann nicht.
Also will ich lieber ein gutes objektorientiertes Modell haben, will ich lieber etwas haben, was auf der Datenbank gut funktioniert.
Da kann es halt unterschiedliche Gründe für geben.
Also je nach Komplexität der Geschäftslogik, je nachdem wie wichtig Query Performance ist, was meine konkreten Probleme sind.
Und das bedeutet, dass ich da jetzt nicht sagen würde, der Ansatz von Domain Driven, also ein reiner orthodoxer Design-Ansatz ist sozusagen falsch.
Er kann halt zum Beispiel zu Performance-Problemen führen, muss man sich halt angucken, ob er das halt tut.
Es wird dann problematisch, wenn ich zum Beispiel ein Legacy-RDBMS-Modell habe.
Wenn ich da nämlich jetzt sage, ich habe halt ein Modell, was halt nicht so aussieht wie mein objektorientiertes Modell, dann kann ich halt versuchen, diese Mapping-Schichten so zu bauen, dass diese beiden Modelle ineinander übergeführt, werden.
Wenn die hinreichend unterschiedlich und komplex sind, wird das wahrscheinlich dazu führen, dass das langsam, schwer verständlich und irgendwie unsinnig wird oder zu viele Nachteile hat.
Und es kann halt auch ein Problem sein, wenn halt die Datenbank von anderen Anwendungen genutzt wird, weil die vielleicht eben ein hübsches relationales Modell erwarten, wobei man da hinzufügen muss, und das hat sich, glaube ich, durch mehrere Episoden schon durchgezogen.
Das ist etwas, wovor ich warnen würde, weil es eben bedeutet, dass wir Daten teilen und das bedeutet, dass wir halt die Simulierung nicht ändern können.
Das heißt, wenn ich jetzt dieses relationale Modell tatsächlich teile, dann werde ich eben dieses relationale Modell nicht mehr ändern können, es sei denn, ich informiere alle Beteiligten anderen Systeme.
Und das führt dann eben dazu, dass diese Modelle de facto nicht mehr änderbar sind.
Und in der Praxis ist das etwas, was mir häufiger begegnet.
Große, komplexe, relationale Modelle, die im Wege stehen, davon Systeme kleinzuhacken, kleinzumachen und besser zu modularisieren.
Deswegen finde ich, wie soll ich sagen, Teilen von Datenbankschemata schwierig.
Und deswegen finde ich, wenn das als Nachteil ist, dass es schwieriger wird, ist es kein so großer Nachteil.
Was das jetzt bedeutet für diese Geschichte, die ich am Anfang genannt habe, ist, ich habe also eine Klasse, keine Entity, die irgendwie Kunden aus einer Kundentabelle repräsentiert oder die Kundentabelle repräsentiert.
Und ich habe eine Anfrage auf diese Tabelle.
Das ist kein Repository.
Und ich will jetzt gerne andere Tabellen mit involvieren.
Das war ja das, was eben diese Episode mit sozusagen motiviert hat.
Aus einer Sicht von Domain-Driven Design würde man sagen, das willst du nicht.
Du hast Aggregates, du hast Repositories, du willst ein klares repräsentiertes Modell.
Du willst halt nicht die Kundentabelle.
Du willst Kunde, Customer Types und Addresses.
Aber das ist halt die Frage, ob das tatsächlich so ist.
Und warum würde ich das mit Domain-Driven Design so machen wollen?
Weil ich dann ein objektorientiertes Design habe, weil ich dann halt Kunden habe.
Ich kann da irgendwie Funktionalität dran tun.
Und wenn ich einen anderen Fall habe, will ich vielleicht anders vorgehen.
Und ich habe dazu so einen Klassiker rausgesammelt.
Ich weiß nicht, ob der in Vergessenheit geraten ist, aber das ist halt so eines von den meiner Ansicht nach grundlegenden Büchern.
Das Team noch mal anders diskutiert.
Das sind die Patterns of Enterprise Application Architecture von dem guten Martin Fowler.
Ich werde ein paar von diesen Patterns vorstellen, die für dieses Thema.
Ich habe irgendwie etwas, was eine Kundentabelle repräsentiert.
Ich habe jetzt eine Anfrage, die andere Tabellen hat.
Wie gehe ich damit um?
Martin Fowler hat verschiedene Patterns, die ich jetzt hier auf so einem Spektrum aufgemalt habe, zwischen objektorientiert und relationales Datenbanksystem.
Wir sind ja jetzt oben in diesem Spektrum.
Wir sagen, wir haben einen objektorientierten Entwurf und wir haben bedauerlicherweise eine relationale Datenbank.
Die hat eigentlich eher stört und wir würden sie am liebsten ignorieren.
Das ist so ein bisschen das, wo wir herkommen.
Und da ist es jetzt eben so, dass in Patterns of Enterprise Application Architecture einmal diese Idee gibt von einem Domainmodell.
Ich sollte noch sagen, ich verlinke noch die Webseite von dem Martin Fowler, wo er zumindest die Abstracts von diesem Patterns hat, also jeweils eine einsätzige Beschreibung.
Aber es gibt eben das Buch und das Buch ist durchaus lesenswert.
Das Buch ist ein Standardwerk und deutlich lesenswert.
Zu Domainmodell hat er geschrieben, ein Objektmodell der Domäne, das hat sowohl Verhalten als auch Daten umfasst.
Ich würde behaupten, das, was wir diskutiert haben aus der DDD-Sicht, ist sowas.
Wir haben jetzt lauter objektorientierte Sachen, wo Verhalten drin ist.
Customer hat irgendwelche Methoden, die Verhalten darstellen.
Order hat irgendwelche Methoden, die Verhalten darstellen.
Der Order Service hat sowas und so weiter und so weiter.
Das ist also das Domainmodell.
Und dann sagt er halt, dass es ein Datamapper gibt.
Das ist eine Schicht von Mappern, die Daten zwischen den Objekten und der Datenbank hin und her transferiert und die beiden möglichst unabhängig voneinander halten.
Das ist das, was nach meinem Empfinden so ein bisschen in der Luft liegt, wo ich jetzt also mit sowas wie einer JPA-Implementierung beispielsweise oder mit einem Mappern in anderen Universen, also nicht JPA, im Java-Universum, halt jetzt Dinge aus diesem Objektmodell transparent in die Datenbank bekomme.
Das heißt, wenn ich mir den Code angucke, sagt er halt im Wesentlichen, okay, wir haben Repositories, wir haben irgendwelche Objekte und diese ganzen Objekte, Repositories, da sehe ich nicht, dass eine Datenbank hinterhängt.
Das heißt, die sorgen dafür, diese Illusion der großen Sammlung von Objekten, die ein Repository hat, die ist tatsächlich eine Relation der Datenbank.
Das merke ich aber nicht.
Ich bin halt auf dieser Abstraktionsstufe von einem Repository.
Und da gibt es jetzt halt noch viele weitere Patterns, auf die ich eigentlich nicht eingehen will.
Also tatsächlich sind in Patterns of Enterprise Application Architecture Patterns, die halt was sagen darüber, wie halt OR-Mapper funktionieren.
Unit of Work habe ich mir rausgeschrieben.
Unit of Work bedeutet, dass ich eine Liste von Objekten habe, die halt irgendwie gerade geändert worden sind und sie dann halt irgendwann zurückschreibe.
Und das ist halt etwas, was ein OR-Mapper halt intern tut.
Das heißt, wenn irgendwas passiert, schreibt er halt die Daten anschließend zurück.
Das, was mich eigentlich eher interessiert, ist halt die Frage, wie denn andere Persistenzansätze aussehen können.
Und da kommt sozusagen der folgende Titel rein.
Also dadurch, dass ich eine Relation der Datenbank habe, würde ich behaupten, ist es vielleicht nicht so schlau für alle Fälle wegen Performance, wegen der anderen Modellierung, tatsächlich jetzt konsequent ein objektorientiertes Modell umzusetzen.
Also gibt es vielleicht Sachen, die dichter in der Datenbank sind.
Das sind die, die ich hier jetzt noch einführen werde und die halt niedriger sind.
Und da habe ich jetzt erst mal Active Record.
Active Record, was eben Ruby on Rails beispielsweise implementiert.
Da bedeutet es halt, dass ein Objekt eine Zeile in einer Datenbank, Tabelle oder View eben repräsentiert und dort auch zum Beispiel das Schreiben in die Datenbank drin hat.
Das heißt, oder die Datenbank-Methoden drin hat.
Das heißt also, ich habe dort ein Objekt, was ich eben zum Beispiel speichern kann, was ich halt bei dem Domain-Modell nicht habe.
Da passiert das halt hinter den Kulissen an der Stelle, wo eine Transaktion beendet wird.
Bei Active Record muss ich das eben selber aktiv steuern.
Und wichtig dabei, ein Active Record hat trotzdem Domain-Logik.
Das heißt, ich könnte jetzt eben sagen, ich habe eine Customer-Tabelle, ich baue so ein Customer Active Record und dieses Ding hat dann irgendwelche Methoden, die fachlich irgendetwas tun.
Und dann habe ich dort eben weitere Methoden, die dafür sorgen, dass ich das in die Datenbank geschrieben bekomme.
Das ist etwas, was also dicht an der Datenbank dran ist, in dem Sinne, dass ich jetzt irgendwie nicht sage, okay, ich transformiere mein Datenbank-Schema in ein komplett getrenntes OO-Schema, sondern es ist eben dicht an der Datenbank, was wiederum impliziert, dass ich halt leichter sehen kann oder steuern kann, was auf der Datenbank passiert.
Und das ist halt in einigen Fällen vorteilhaft.
Also diese Abstraktionsschicht, es gibt in der Praxis diese Probleme, dass man halt sich hinstellt und sagt, warum erzeugt jetzt mein OER-Mapper diese Query?
Das Problem habe ich nicht, wenn ich die Query halt explizit hinschreiben kann und halt dicht an der Datenbank bin und mehr Kontrolle habe.
In dem Zusammenhang gibt es noch das Transaction Script.
Das organisiert die Geschäftslogik als Prozeduren, wo jede Prozedur durch einen Request, durch die Präsentation getriggert wird.
Das heißt also, was ich im Prinzip dort mache, ist, ich nehme ein Pattern wie beispielsweise Active Record oder das, was ich noch zeige für Persistenz und dann habe ich halt relativ stumpf etwas, wo jetzt irgendwie steht, okay, ich mache irgendetwas.
Im Extremfall direkte Queries auf die Datenbank.
So und da ist jetzt eine mögliche Antwort auf das, was dieser Mensch ja gefragt hat.
Also ich habe jetzt zum Beispiel ein Active Record für Kunden.
Ich möchte jetzt eine Query haben, die halt mehrere Tabellen zusammenfasst.
Schreibt das doch in einem Transaction Script einfach hin.
Ich diskutiere dieses Pattern halt sehr gerne, weil das das Beispiel dafür ist, dass die drei Schichten halt nahezu kollabieren.
Also ich habe dann halt die UI, die Logik und die Persistenz in einer Methode.
Also ich hätte jetzt einen TDP-Händler, der jetzt sagt, ich mache eine Query auf der Datenbank, vielleicht mit so etwas wie Active Record, aber vielleicht halt auch sozusagen handgeschrieben.
Ich schreibe irgendwas in die Datenbank und mache dazwischen noch Berechnungen und bin dann fertig.
So und da würde man jetzt sagen, naja, also drei Schichten sind halt universell akzeptiert.
Warum würde man das machen wollen?
Es ist halt dann günstig, also es hat zwei Vorteile.
Das eine ist, ich sehe halt direkt, was auf der Datenbank passiert.
Das heißt, ich kann die Queries handoptimieren und ich sehe ohne Zweifel, was dort gerade passiert.
Das andere ist, es ist halt relativ einfach.
Also ich hatte irgendwann mal auch diese Frage mit, was ist denn die optimale Anzahl an Schichten, wo glaube ich tatsächlich die Frage war, sind drei Schichten genug oder sollen es mehr oder weniger sein?
Und das hängt halt vom Fall ab.
Also ich habe ein Projekt gehabt, wo nach meinem Empfinden die nur die Aufgabe, also nur in Anführungsstrichen die Aufgabe darin bestand, irgendwelche Daten, die über HTTP angeliefert werden, in die Datenbank zu schreiben.
Und wenn ich es halt entwickeln müsste, würde ich mir glaube ich tatsächlich wünschen, dass ich dann Transaction Script habe und auch gar kein Active Record oder so, sondern ich würde mir ein Insert Statement schreiben und das Insert Statement halt anfüllen.
Das würde ich halt deswegen machen, weil ich habe halt keine Logik, also so jedenfalls die Aussage, ich will nur in die Datenbank schreiben und ich habe dann volle Kontrolle über dieses Statement und habe da halt nicht, muss nicht raten, was jetzt irgendwie da passiert und was irgendein OER-Mapper tut.
Wenn ich das versuchen würde, mit einem OER-Mapper umzusetzen, müsste ich halt Klassen schreiben, müsste halt sowas wie ein Repository oder so bauen und es hat keinen Wert.
Also weil da ist halt tatsächlich keine Logik, es ist halt nur Daten verschieben und das ist halt nicht der Grund, warum Transaction Script vielleicht gar nicht so schlecht ist.
Hier ist jetzt so ein bisschen ein Mismatch und zwar ist es halt so, dass wir, also Mismatch ist vielleicht übertrieben, ich würde eher sagen, es ist ein Kompromiss.
Also wir sind noch nicht ganz unten bei der relationalen Datenbank angekommen, wo wir direkt jetzt Datenbank-Querys sozusagen machen.
Wir sind aber etwas weiter weg von einem reinen objektorientierten Konzept, weil wir eben sagen, ein Active Record ist eine Tabelle oder repräsentiert eine Tabelle, aber da ist irgendwie noch Geschäftslogik drin.
Für sowas, wo ich tatsächlich nur Daten irgendwo hinschreiben will, ist ein Active Record eigentlich übertrieben, weil da ist eben keine Logik.
Deswegen gibt es zum Beispiel sowas wie ein Row Data Gateway.
Das ist ein Objekt, das als Gateway, also Gateway ist so ein Basis-Pattern, was der Martin Fowler in diesem Buch einführt, was letztendlich eben eine Pforte zu einem anderen System oder sowas ist.
Und ein Row-Data-Gateway sagt, ich habe ein Objekt, das halt als Gateway zu einem einzelnen Rekord in der Datenbank gehört, und dann gibt es halt eine Instanz pro Zeile in der Datenbank.
Was halt bedeutet, das ist halt so wie Active Record, also ich hätte jetzt eben ein, weiß ich nicht, Kunde-Row-Data-Gateway, und dieses Kunde-Row-Data-Gateway, da hätte ich jetzt eben Kundenobjekte wichtig, und das ist der Unterschied, ich hätte hier halt keine Logik, und das passt vielleicht besser als Active Record zu Transaction Script, weil ich eben im Transaction Script sage, ich habe wenig Logik, ich will hier halt in dieses Skript reintun und nichts Großes sozusagen sagen.
Jetzt muss ich mal schauen, da ist ein Kommentar von Twitch, von Jojo, wo die Frage ist, muss es immer DDD sein, oder würde Dynamic Domain Model auch ausreichen in bestimmten Fällen?
Das passt gerade zu dem, was ich halt diskutiere, glaube ich, ganz gut dazu.
Also erstmal vielen Dank für die Frage.
Was ist ein Dynamic Domain Model?
Also Dynamic heißt blutleer, und das ist so ein bisschen etwas, ich würde sagen, eine Beschimpfung, das ist vielleicht der richtige Begriff, den Menschen gewählt haben für Modelle, die halt nicht wirklich objektorientiert sind, also wo ich eben Objekte habe, die nur Werte darstellen, wo ich aber keine Logik drin habe.
Und das, was wir jetzt hier diskutieren, Row Data Gateway, du hast die Frage sozusagen genau zur richtigen Zeit gestellt, ist so ein Dynamic Domain Model.
Und der Grund, warum ich diese Episode machen wollte, ist, weil ich eben behaupten würde, dass es Umgebungen gibt, ich hatte es gesagt, ich habe einen HTTP-Service, der HTTP-Service nimmt Daten entgegen, irgendwie aus der Fläche, schreibt in der Datenbank, da ist vielleicht eine Transformation, aber das hat keine echte Logik.
Warum würde ich da halt ein echtes objektorientiertes Modell mit ganz viel Einkapselung bauen?
Ich werde keine Methoden schreiben, und dann kann ich eben auch gleich einen einfachen Ansatz wählen.
Was also bedeutet auf deine Frage, muss es immer DDD sein?
Nein, es muss eben nicht DDD sein, und wir sind hier jetzt auf dem Spektrum der Architekturen, der durch die relationalen Datenbanken beeinflusst sind, nicht durch Objektorientierung.
Also dieses Transaction Script kann ich halt genauso gut in, weiß ich nicht, Basic oder Pascal oder so schreiben, nicht konzeptionell, muss dafür keine Objekte haben, ich kann halt einfach Prozeduren schreiben, Static-Methoden oder was auch immer.
Das war die Frage, dann hat der Urs, schreibt, bei uns delegieren die HTTP-Händler den Call an ein Transaction Script, Command, genau, Command ist ein, genau, sollte ich gleich sagen, welches dann direkt alles macht, was passieren muss, und somit als eine Kombi aus Service und Aggregate agiert.
Da sind jetzt drei verschiedene Patterns drin, erst mal Command, Command ist halt ein Gov-Pattern, das sagt halt, dass ein Objekt sozusagen Änderungen darstellt.
Ich weiß nicht, ob das hilfreich ist in diesem Kontext, also vielleicht aus, also ich würde sagen Transaction Script ist der präzisere Begriff.
Ich würde mich dagegen wehren, ein Transaction Script als eine Kombination aus Service und Aggregate zu bezeichnen.
Also entweder ich habe ein echtes objektorientiertes Modell mit viel Logik, dann brauche ich Aggregates und Services, und die Aggregates sind eben die Sachen, wo halt Kundenlogik drin ist, oder nicht, Bestelllogik.
Ich kann eine Bestellung fragen, wie schnell sie geliefert wird, wobei ich weiß nicht, kann ich das wirklich?
Vielleicht brauche ich da komplexere Logik, die halt mehrere Objekte zusammenholt, aber nicht.
Ich könnte jetzt eben sagen, eine Bestellung weiß, wann sie geliefert wird, weiß sicher, was sie wert ist, aber das ist nicht viel Logik.
Vielleicht kann sie eine Mehrwertsteuer berechnen, solche Geschichten, und das wäre jetzt im Aggregate drin, und im Service wäre jetzt das drin, was zum Beispiel sagt, okay, ich muss halt den Kunden modifizieren und die Bestellung, also ein Bestellprozess.
Transaction Script enthält zwar vielleicht dieselbe Logik, also eine Logik, um eine Bestellung durchzuführen, aber macht das halt einfach direkt mit Datenbankoperationen oder mit solchen einfachen Dingen wie eben einem Raw Data Gateway und nicht mit komplizierteren Sachen, und das bedeutet, es ist halt näher an der Datenbank, deswegen hatte ich es hier ja aufgemalt, und es ist eben etwas anderes.
Vielleicht dazu grundsätzlich, also ich finde es genauso unpassend, glaube ich, einen Datenimport mit so einem DDD-Modell zu versuchen zu erschlagen, wie ich es schwer vorstellbar finde, ein Transaction Script zu benutzen, um tatsächlich eine Bestellung durchzuschicken, also weil ich halt erwarten würde, dass bei der Bestellung die Logik so kompliziert ist, dass ich das anders machen möchte und das besser aufteilen will.
Was hat Urs noch geschrieben?
Also für Konsistenz sorgt, ja genau, also, ja, guter Punkt, da ist halt so ein bisschen diese, ich hatte es vorhin gesagt, aus der relationalen Sicht kann ich ja eine Transaktion machen, die halt die halbe Datenbank ändert und halt von einem konsistenten Zustand in einen anderen überführt.
Das, was ich halt so interessant finde, und ich verlinke dazu nochmal, wir haben mehrere Episoden zu diesem Konsistenzthema, das, was ich so interessant finde, ist, dass man im Design sagt, die Erwartungshaltung ist, dass nur ein Aggregate Konsistent sein wird und eben nicht ein Service.
Hier, in dem Modell, über das wir hier reden, würde man, also Transaction Script sagt es halt schon, erwarten, dass halt mehrere, eine oder mehrere Datenbank-Sachen, die halt in dem Transaction Script sind, insgesamt eine Transaktion sind, und die könnten halt deutlich mehr Tabellen umfassen als ein Aggregate, sodass also auch dort die Beziehung oder die Konsistenzbedingungen oder der Scope der Konsistenz möglicherweise größer ist.
So, was steht da weiter?
Natürlich verwenden die Commands Metall-Geologik aus anderen Modulen, um nicht immer alles neu zu erfinden.
Ja, okay.
Wobei, nicht, also dann könnte es schon sein, dass vielleicht ein Object-Oriented-Modell besser ist.
Dynamik-Domain-Models sind viel flexibler bezüglich Verhalten, wenn es man auf ihnen ausführt, zum Beispiel, wenn für unterschiedliche Tenants andere Logik auf denselben Daten ausgeführt werden soll.
Ja, das stellt in Abrede, dass halt im Kern, dass halt Objektorientierte Modellierung funktioniert und flexibel ist.
Wenn man sich die Gov-Patterns anguckt, da gibt es sowas wie Strategy-Patterns, wo ich eben abhängig von irgendetwas unterschiedliche Algorithmen reinpacken kann.
Da habe ich einfach ein Ding, was halt eine einheitliche Schnittstelle hat, aber ich unterschiedliche Implementierungen zurückgebe, abhängig von irgendwelchen Sachen, und dadurch habe ich eben eine Strategy, die ich austauschen kann.
Ich kann Flexibilität erreichen durch Vererbung, ich kann eben sagen, es gibt einen Kunden, da gibt es einen Großkunden, das ist eine Spezialisierung, der fällt sich an bestimmten Sachen anders, sodass ich halt nicht unterschreiben würde, dass ein Dynamik-Domain-Modell flexibler ist.
Ich kann das Gegenteil behaupten, weil ich halt durch objektorientierte Mechanismen mehr Möglichkeiten habe, Logik flexibel zu modellisieren.
So, Jojo hat geschrieben in die DD, wie viel Logik darf in Services sein?
Das ist einfach beantwortbar, Services beinhalten halt die Logik, die ich nicht in Entities oder Aggregates unterbringen kann, also die halt übergreifend ist.
Oder darf die Logik nur in den Aggregates selber sein?
Das habe ich beantwortet.
Die Services koordinieren nur, was macht man, wenn man Logik hat, die zwei Aggregates bearbeitet, wo packt man das hin?
Eben in einen Service, genau.
Also Logik, die hat in zwei Aggregates bearbeiten gehören, in einen Service.
Gibt es da eine Empfehlung oder kommt es darauf an?
Es kommt darauf an, ist man immer auf der sicheren Seite, aber der nächste Satz kommt darauf an, ist halt der Wichtigere.
Da hast du schon recht, aber in FP ist es einfach eine weitere Funktion.
Ja, aber wir reden, also nicht im funktionalen Programm, schreibt der Urs Enzler gerade, Funktionale Programmierung ist hier ausgenommen, nicht?
Also das Ganze, was ich hier erzähle, ist halt im Kern sind das objektorientierte Patterns.
Ich glaube, das würde halt in einer funktionalen Umgebung nochmal anders aussehen.
Gut, danke für die Fragen.
Kommen wir zurück zu dem, genau, zu den Patterns hier.
Also ich sagte gerade, ein RowDataGateway wäre eben sowas, wo ich halt eine Klasse habe, Konto, die hat eine Zeile oder Kunde, die hat eine Zeile in der Kundentabelle repräsentiert und dann habe ich halt ein triviales Mapping, weil ich halt direkt diese Sachen halt umsetze und das ist eine Möglichkeit, im Gegensatz zu ActiveRecord habe ich dort eben keine Logik, also sozusagen ein Dynamic Domain Modell und das andere Pattern, was es da gibt, ist das TableDataGateway, da habe ich ein Objekt, das halt eine Tabelle repräsentiert und eben alle Zeilen dieser Tabelle repräsentiert.
Das heißt, ich kann dorthin gehen und kann jetzt, also habe zum Beispiel ein KundeGateway für die Tabelle Kunde und hätte jetzt dort Methoden, die halt primitive Datentypen übernehmen, also nicht eine Insert beispielsweise, könnte ich einfach primitive Datentypen übernehmen und ich kann zurückgeben irgendeine generische Datenstruktur, also eine Map oder so, wo ich dann für ein Attribut jeweils die Werte rauslesen kann.
Dadurch kann ich jetzt eben in einem TransactionScript beispielsweise, könnte halt ein TableDataGateway benutzen, um halt diese ganz konkreten SQL-Queries rauszubekommen aus dem TransactionScript, ohne dabei halt viel komplizierte Logik einzubauen, so wie in einem Datamapper, der die beiden Sachen ineinander überführt.
Und das sind also, sprich, bei dem TableDataGateway habe ich kein Objekt, was halt die konkrete Tabellenzeile repräsentiert, das habe ich beim RowDataGateway, sondern da muss ich eben dann die Daten aus generischen Datenstrukturen rauslesen.
Jolo schreibt, das Thema von heute ist übrigens sehr spannend.
Vielen Dank, freut mich.
DDD und andere Abbildungen in die Datenbanken ist ein Top-Thema und er schreibt weiter, da kannst du gerne nochmal Videos zu machen.
Vielen Dank, also ehrlich gesagt, ich hatte das nahezu für ein Nischenthema gehalten, aber offensichtlich hat es dann ja funktioniert und das ist ja auch ein bisschen die Idee, jetzt nicht irgendwie jede Woche die ganz großen Themen sozusagen zu schwingen.
So, was das jetzt insgesamt, also damit bin ich sozusagen fertig mit diesen ganzen Patterns, die es halt in dem Kontext gibt.
Was das halt insgesamt bedeutet, ist, es gibt eben ein Mismatch zwischen Objektmontierung und Relational.
Und ich glaube, der wesentliche Punkt ist dabei, dass ich mir nicht so sicher bin, ob man das eine oder das andere möchte als führendes Modell und das ist ja eigentlich die zentrale Frage.
Also wenn ich das mit Domain-Driven Design mache und es krass mache und nahezu übertreibe, dann sage ich halt, objektmontiertes Modell, wenn ich dasselbe mache mit sowas wie RowDataGateway oder TableDataGateway, dann habe ich eben dieses sehr klar Relationale Modell drin und ich muss mich jetzt irgendwie positionieren und muss mir halt überlegen, welches von beiden der richtige Ansatz ist.
Und ich würde nicht sagen, dass halt der eine, und deswegen finde ich das mit dem Dynamic Domain-Modell auch schwierig, das kann halt ein guter Ansatz sein.
Patterns of Enterprise Application Architecture enthält noch viele weitere Patterns, auch zum Beispiel für sowas wie Web-Schichten oder so, also insgesamt lesenswertes Buch.
Ich glaube, dass die Sachen, die ich hier gezeigt habe über Datenbanken, Relationale Datenbanken ist, glaube ich, ein bisschen so das Wichtigste oder das, was ich persönlich am wichtigsten finde.
Damit haben wir das Thema, glaube ich, eigentlich erledigt.
Wir jetzt nochmal kurz eingehen auf Dokumenten-Datenbanken, das sind ja NoSQL-Datenbanken und populäres Beispiel ist halt MongoDB und die haben eben, wie der Name es verrät, Dokumente, die sind größer, also das sind typischerweise so JSON-Datenstrukturen, die ich halt direkt abspeichern kann und die werden halt in MongoDB beispielsweise als BSON gespeichert, also mit einer binären Codierung und damit habe ich dort was anderes als eine Tabelle, also eine Tabelle kann man sich echt so vorstellen wie eine Excel-Tabelle.
Ein Dokument ist eben im Wesentlichen so ein JSON-Ding und da kann man sich jetzt vorstellen, dass ein Kunde mit so etwas, wie gehe ich denn mit der Adresse eigentlich um und da ist es eben so, dass ich halt natürlich die Adresse in das JSON-Dokument reinschreiben kann.
Ich kann sogar mehrere Adressen als Array haben und ich kann die halt irgendwie auch, Embedding heißt das, dort irgendwie reinbekommen.
Das heißt also, was ich jetzt machen kann ist, ich könnte sagen, ein Aggregate aus DDD wird ein Dokument in MongoDB und ich glaube auch, dass das so der richtige Ansatz ist, weil ich halt eben bei Dokumenten-Datenbanken zwischen Dokumenten so einen Bruch habe.
Also ich kann schon von einem Dokument auf ein anderes verweisen, aber ich kann halt nicht diese Dinge tun, von denen ich gesprochen habe.
Gib mir bitte den Umsatz aller Kunden aus Hamburg, da kriege ich dann in der relationalen Datenbank das hin, diese fünf Tabellen irgendwie zu joinen und dafür zu sorgen, dass ich das halt mit irgendwie Query-Optimizern und irgendwelchen Sachen eben so hinbekomme, dass möglichst wenig Daten gelesen werden und möglichst schnell das Ergebnis da ist.
Wenn ich jetzt Kunden und Bestellungen habe und ich habe dazwischen so eine Dokumentenbeziehung, kann ich sicher irgendwie versuchen, alle Kunden rauszubekommen, die halt aus Hamburg sind, aber dann werde ich wahrscheinlich, so mein Verständnis, im nächsten Schritt alle Bestellungen laden müssen und halt irgendwie die aufsummieren müssen und das wird vermutlich aufwendiger, es hinzuschreiben und wahrscheinlich auch weniger performant.
Und das bedeutet halt, dass ich dort vielleicht sogar näher dran bin an dem objektorientierten Modell, aber dafür eben Flexibilität verliere.
Also wenn ich jetzt die dort, oder anders gesagt, wenn ich normalisiertes Datenmodell habe in der relationalen Datenbank, kann ich halt fast beliebige Dinge damit tun und sie halt auch schnell machen durch Indizes.
Wenn ich mich bei der Dokumenten-Datenbank vertue, bei der Modellierung, werde ich halt tatsächlich alle Voraussicht nach irgendwann sozusagen auf die Schnauze fallen.
Dafür gibt es Vorteile.
Also ich kann Daten flexibel speichern.
Ich hatte ja gesagt, dass ich vielleicht bei Kunden so Großkunden haben möchte als Sonderfall.
Die haben halt mehr Attribute.
Das ist halt kein Problem.
Ich kann halt andere Attribute hinzufügen, kann halt in der Kunden-Collection dann Daten haben mit unterschiedlichen Informationen und unterschiedlichen Attributen.
Aus der relationalen Sicht ist das so ein bisschen chaotisch.
Also mich würde man dem wahrscheinlich vorwerfen, dass das chaotisch ist, weil relational halt sagt, dass die Daten alle gleich aussehen sollen und Konsistenzbedingungen hat.
Durch diese stärkeren Brüche kriege ich leichtere Skalierbarkeit.
Also was ich jetzt machen kann, ist, ich kann eben die Kunden auf mehr Rechner verteilen, weil ich ja im Wesentlichen nur darauf zugreife, indem ich halt sage, ich hätte gern den mit dieser ID.
Dann kann ich halt zu irgendeinem Rechner gehen, zu irgendeinem Server und kann mir den richtigen raus holen.
Ich habe nicht mehr so ein Problem, wo ich sage, wenn eine Query kommt, umfasst die plötzlich die gesamte Datenbank.
Weil ich eben diese Joins, wo irgendwie verschiedene Tabellen zusammen gemerged werden kann, weil ich das halt vermeide.
Was halt bedeutet, und da kommt wieder dieses mit, wie die Datenbank die Architektur formt.
Ich werde dort also wahrscheinlich bei einem Modell ankommen, wo ich grobgranulare Strukturen habe, eben solche Dokumente, und wo ich mir vorher Gedanken machen muss, wie die genau aussehen, wie meine Relationen aussehen und wie das halt funktionieren soll.
Das werde ich wohl im Vorhinein machen müssen.
Ich kann das eben nicht flexibel handhaben und halt ändern.
Letzter Satz, um das noch kurz zu sagen.
Eine Sache, die man noch, also MongoDB hat das, aber auch die meisten relationalen Datenbanken haben das, man kann dann noch irgendwie so in dieses Spektrum gehören, meinen Sie nachher auch noch Volltext-Suchmaschinen, die vielleicht insbesondere für Queries gut sind und logischerweise, also Suchen, die ich halt über Texte mache, da kriege ich damit halt sehr gut hin.
Und diese Volltext-Suchmaschinen kann ich jetzt ergänzend eigentlich nutzen für die verschiedenen Datenbanken, um eben noch zusätzliche Indizien zu haben oder auch möglicherweise, also sprich, ich kann sagen, hier ist ein Volltext-Index über meine relationalen Datenbanken und wenn du halt irgendein Datum suchst, dann gehe gerne auch zu dem Volltext-Index oder ich kann sogar diese Volltext-Datenbanken benutzen und sowas wie Elastic und kann die eben auch nutzen, um halt Dokumente zu speichern.
Das geht auch.
Gut, dann würde ich sagen, vielen Dank für die Aufmerksamkeit und wir haben nächste Woche tatsächlich eine Episode geplant, findet man auch bei uns auf der Webseite.
Und zwar ist das agile Dokumentation mit Liam Berg.
Die Episode macht Ralf dann.
Dann würde ich sagen, vielen Dank für die Aufmerksamkeit, schönes Wochenende und vielleicht bis nächste Woche.