# 3-Schicht-Architektur mit generischem Ansatz



## oopexpert (24. Aug 2011)

Sehr geehrte Forum-Mitglieder: Ich bin frustriert...

Seit geraumer Zeit bin ich als Freelancer für eine kleine Abteilung eines großen Konzerns tätig. Wir entwickeln Planungssoftware für Teilebedarfe der Produktion. Als ich mir die Architektur genauer anschaute, traute ich meinen Augen nicht. Die Schnittstelle zum Rich-Client, die der Server bereitstellt, besteht aus 6 Methoden:


List<Object> getAll(Class class)
 List<Object> getAllByName(Class class, String name)
 List<Object> getAllByIds(Class class, UUID id1, ...)
 List<Object> getAllByUser(Class class)
 Object getById(Class class, UUID id)
 saveOrUpdate(Object o)

Grundsätzlich gefallen mir die generischen Methoden zum Laden relativ gut, auch wenn sie ein wenig Granularität vermissen. Aber was aus meiner Sicht absolut fragwürdig ist, ist die Methode saveOrUpdate. Nochmals zur Erinnerung: Diese Methode befindet sich auf dem Rich-Client. Einen weiteren Kick bekommt dieses Design, wenn man sich die Delegationen dieser Methode genauer anschaut. Im Grunde wird über alle Schichten bis zu der Hibernate-Persistenz delegiert, welche schlussendlich ein saveOrUpdate durchführt. Die technologischen Schichten sehen wiefolgt aus: 


 Rich-Client angebunden mit Spring-Remoting
 Tomcat-Server, natürlich mit Spring konfiguriert
 Hibernate
 Oracle-DB

Da auf dem Appl-Server nur generische Elemente existieren, ist die Business-Logik komplett im Client. Die "Fachklassen" sind Beans ohne Logik, mal davon abgesehen, dass es Hibernate-gemappte Objekte sind, die durch die Weltgeschichte gereicht werden.

Nach erbitterten Diskussionen, kamen einige inhaltliche Zugeständnisse, die aber den generellen Ansatz verteidigen und vor allem zu sehr seltsamen Implementationen führen.


Validierung findet klassenbezogen auf dem Server statt.
Alle Anfragen an den Server werden im Moment über "synchronized" an der Methode "saveOrUpdate" synchronisiert.
Es kamen von anderer Seite Ideen auf, alle ändernden Operationen an den Fach-Beans (set, add) mit Locking-Informationen anzureichern, damit man das synchronized nicht mehr benötigt.

Eine weitere Sache ist, dass konzeptionell Lost-Updates nur in besonderen Situationen abgefangen werden sollen. Eine generelle Vorgehensweise gibt es nicht.

Im letzten Monat wurde ein Modul entwickelt, welches auf der Seite des Clients Transaktionen suggeriert. Es gibt eine sogenannte Kontext-Klasse, über die Objekte angefordert angefordert werden können. Die Original-Beans werden gekapselt und mit einem Dirty-Mechanismus dekoriert. Schlussendlich wird auch hier eine Methode mit dem Namen "saveOrUpdate" aufgerufen, die nicht nur ein einziges Objekt überträgt, sondern alle neu erzeugten und geänderten und für jedes einzelne Objekt ein Hibernate-saveOrUpdate aufruft. Seitdem sind alle DB-Constraints Defferable und werden bei Einleitung der Transaktion Deffered, weil man die Reihenfolge der Objekte nicht sicherstellen kann ...

Lange Einleitung, kurze Frage: Macht man das heute so???

Freue mich über jeden Beitrag.


----------



## nocturne (25. Aug 2011)

Richtig, die Präsentationsschicht darf keine Datenbankänderungen durchführen. 

Diskussionen sind begrenzt gut. "Erbitterte Diskussionen", zersetzen das Teamwork und sind der tot der Freelancerkarriere! Als Freelancer ist man sowieso in der Defensive, setz' einfach die delegierten Aufgaben entsprechend der Betriebseigenheiten, sauber um. 

Mal von Freelancer zu Freelancer: das ist natürlich unangenehm, trotzdem hat sich der Betrieb bewust gegen einen Freelancer mit Leitungsbefugnis entschieden. Sicher dich unbedingt rechtlich ab, mach aber deine Kritik nicht zum Zentrum der Zusammenarbeit. 

Solltest du sagen: Das ist ethisch/rechtlich o.ä. nicht tragbar musst du die Probleme auf die nächst-höhere-leitung Eskalieren, dazu brauchst du aber Rückhalt aus dem Betrieb.


Kannst ja pn schicken.
Grüße


----------



## Effad (6. Sep 2011)

Wahllose Gedanken:




> Nach erbitterten Diskussionen, kamen einige inhaltliche Zugeständnisse


 Das klingt schwer danach, dass nun ein "Kompromis-Design" gefunden wurde. Meiner Erfahrung nach ist das menschlich gut, technisch aber schlecht. 
Hibernate würde ich keinem größeren Projekt einsetzen. Wir hatten das und sind äußerst schwer auf die Nase gefallen. Die zusätzlichen Probleme, die Hibernate bringt rechtfertigen einfach die Vorteile. 
Zunächst sieht alles total super aus, statt der dummen DB-Abfragen hat man nun nur mehr Mapping und Objekte, kurz keinen Paradigmenbruch mehr zwischen OOP und DB. Im Detail hast aber nur Probleme, weil das Mapping halt je nach Anforderungssituation anders sein müsste und die Query-Performance nicht gut beeinflussbar ist, etc. pp.
Siehe dazu auch: Coding Horror: Object-Relational Mapping is the Vietnam of Computer Science
 Ich würde über der Datenbank RESTful Webservice ansiedeln und auf diese mit dem Rich-Client zugreifen.


----------



## nocturne (7. Sep 2011)

Guten Morgen.

Na ein Kompromiss ist ein freiwilliger Zustand den beide Anstreben. Nennen wir es doch "Unprofessionell-Design" oder "Inkonsequent-Design".

Ich bin immer traurig wenn jemand gegen ORM schreibt. Nicht wegen den Basic-ORM's sondern wegen den ganzen Plugins die es gibt (Validator, Enver, Ehcache, Businessobject). Und wegen der leichten Modularität mit z.B.: Plugins.


----------



## maki (7. Sep 2011)

Verstehe die Kritik an Hibernate bzw. ORM im allgemeinen auch nicht bzw. denke dass sie andere Ursachen hat als angeblich schlechte ORMs.

Sobald man Objekte auf RDBMS mappen will, braucht man ein ORM, ob man es sich selber baut oder zu einem vorhandenem Framework greift spielt dabei keine Rolle.

Ein ORM entbindet einen aber nicht davon, RDBMS, OOP und das Mapping zwischen Objekten und RDBMS zu verstehen, ORM ermöglichen keine Ingoranz zu diesen themen, sondern setzen dieses Wissen voraus und bietet viele Dinge schon fertig. Ist das nicht der Fall erlebt man in nachhinein böse Überraschungen.. und ja, ORM ist ne komplexe Sache, da mit fehlenden Grundlagen einzusteigen und gleich drauflos zu programmieren hilft keinem.

@oopexpert 
Das hört sich nicht gut an 
Je generischer eine API ist, umso schwieriger ist die Verwendung, aber umso einfacher die Wiederverwendung, diese beiden Aspekte stehen sich immer entgegen. 
Da ist eine konkrete, spezifische, aussagekräftige & eingeschränkte API besser als eine generische die alles erlaubt aber nichts aussagt und sehr umständlich zu nutzen ist, ausser man baut ein generisches Framework


----------



## oopexpert (7. Sep 2011)

Noch so ein paar Dinge die in der Architektur zu finden sind:

- primitiver Nachbau der Criteria-API von Hibernate auf dem Rich-Client
- Austausch der Hibernate-Proxies durch Eigenimplementation, weil die "Originalen" dem eigenentwickeltem generischen Ansatz der Architektur entgegenstehen
- Überprüfung der Fachlichkeit innerhalb der Transaktion NACHDEM alle Änderungen durchgeführt wurden ("Wird das Objektmodell inkonsistent?" vs. "Ist das Objektmodell inkonsistent?") --> Datenbank Rolback bei fachlichem Fehler führt zu inkonsistenten Hibernate Objekten.
- Abstrakte Klassen im Fachmodell werden als sehr kompliziert empfunden

Letzterer Punkt wurde angesprochen, weil ich über "Rechnung nach Datum", "Letzte Rechnung", "Vorletzte Rechnung" eine Abstraktion "RechnungSelektion" gelegt habe. Die Elemente sollen auch persistent gehalten werden können.

Wie sind solche Dinge zu bewerten?

Die Begriffe "Kompromiss-Design", "Inkonsequent-Design" und "Unprofessionell-Design" zeigen mir ja schon die Tendenz in aufsteigender Eskalation. Ich das Gefühl, zumindest hier vorort, dass keiner die Probleme sieht, bzw. die aktuen Problemen führen zu Lösungen, die ihrerseits wieder Probleme verursachen. Niemand macht die Fallgruben zu, sondern es wird immer herumprogrammiert.


----------



## Effad (7. Sep 2011)

oopexpert hat gesagt.:


> - primitiver Nachbau der Criteria-API von Hibernate auf dem Rich-Client
> - Austausch der Hibernate-Proxies durch Eigenimplementation, weil die "Originalen" dem eigenentwickeltem generischen Ansatz der Architektur entgegenstehen
> - Überprüfung der Fachlichkeit innerhalb der Transaktion NACHDEM alle Änderungen durchgeführt wurden ("Wird das Objektmodell inkonsistent?" vs. "Ist das Objektmodell inkonsistent?") --> Datenbank Rolback bei fachlichem Fehler führt zu inkonsistenten Hibernate Objekten.


Lauter Hibernate-Probleme, bzw. Probleme, die du nicht hast, wenn du gleich auf ORM "verzichtest".

Warum wohl hat man die Criteria-API nachgebaut? War die von Hibernate wohl nicht genau auf das Problem passend (hatten wir auch... war ein Drama...).

Die Hibernate-Proxies musste man auch austauschen? Weil die der Architektur entgegenstehen? D.h. Hibernate gibt eine Architektur vor, das System gibt aber was anderes vor, also versucht man Hibernate "hinzubiegen". Das bezeichne ich gern als "Framework überdehnen".

Und inkonsistente Hibernate-Objekte gibts eben auch nur, wenn man auf Biegen und Brechen Java-Objekte haben will, die "automagisch" mit der Datenbank verbunden sind.


----------



## oopexpert (7. Sep 2011)

Weitergehende Info, da dass von mir wahrscheinlich klar genug dargestellt wurde:


> Warum wohl hat man die Criteria-API nachgebaut? War die von Hibernate wohl nicht genau auf das Problem passend (hatten wir auch... war ein Drama...).


Criteria-Abfragen leben auf dem Server innerhalb einer Hibernate-Session. Die eigens nachgebaute Criteria-API lebt auf dem Client. Die Abfrage wird zum Server geschickt und dann auch in einer Eigenimplementation in ein HQL umgewandelt. Die Eigenentwicklung kann bei weitem nicht das, was Criteria kann.


----------



## oopexpert (7. Sep 2011)

> Die Hibernate-Proxies musste man auch austauschen? Weil die der Architektur entgegenstehen? D.h. Hibernate gibt eine Architektur vor, das System gibt aber was anderes vor, also versucht man Hibernate "hinzubiegen". Das bezeichne ich gern als "Framework überdehnen".


Die Abhängigkeiten sind eklatant. Die Hibernate-Objekte werden werden zum Client geschickt. Dort steht keine Session zur Verfügung. Bei normalen Hibernate-Objekten kommt dann bei nicht initialisierten Assoziationen eine LazyInitializationException. Diese Fehlermeldung resultiert aus einem nicht an eine Session gebundenen Proxy. Und genau der wird auf dem Client durch einen eigenen ersetzt, der das Lazy-Loading über Remote realisiert. Grundsätzlich finde ich es gut, dass man ohne sich um Infrastruktur kümmern muss, über den Objektgraphen navigieren kann. Ich käme aber nie auf die Idee, halb-initialisierte Hibernate-Objekte durch die Gegend zu schicken, und dann als Lösung für die LazyInitializationException eigene Proxies zu implementieren...


----------



## Effad (7. Sep 2011)

oopexpert hat gesagt.:


> Grundsätzlich finde ich es gut, dass man ohne sich um Infrastruktur kümmern muss, über den Objektgraphen navigieren kann.


Und genau darin liegt IMHO das Problem: Das geht in Wirklichkeit nicht. 

Wenn der gesamte Objektgraph (i.e. die Datenbank) nicht klein genug ist, kann man nicht alle Objekte auf einmal in den Speicher laden.

Also macht man ein kluges Mapping, damit man nur jene Teile bekommt, die man sicher braucht. Wenn man dann aber im Graph weiter navigiert, muss man wieder nachladen und bekommt das n+1 SELECT Problem.
Nun kann man für eine Situation vielleicht noch ein Mapping finden, in der genau der richtige Teil des Objektgraphen geladen wird, für viele verschiedene Situationen aber nicht.

So wie man seine Tabellen je nach Situation unterschiedlich "zusammenjoined" müsste man auch in Hibernate je nach Situation unterschiedliche Teile des Graphen rausbekommen. Man hat aber in der Regel nur 1 Mapping.


----------



## maki (7. Sep 2011)

> So wie man seine Tabellen je nach Situation unterschiedlich "zusammenjoined" müsste man auch in Hibernate je nach Situation unterschiedliche Teile des Graphen rausbekommen. Man hat aber in der Regel nur 1 Mapping.


Naja, wofür gibt es eigentlich fetch joins und fetch groups?

Sehe das immer noch als Fehlbedingung Hibernates an, wohl aus unwissenheit, nicht als grundsätzliches ORM Problem.

Die Probleme des TS sind sicherlich nicht in Hibernate begründet... 
Mal ehrlich, den Obejktgraphen nach dem Commit zu validieren ist ja wohl ziemlich das idiotischte was man machen kann, das ist auch kein Hibernate Problem...


----------



## oopexpert (7. Sep 2011)

> Und genau darin liegt IMHO das Problem: Das geht in Wirklichkeit nicht.


Das geht wunderbar mit dem navigieren über den Objektgraphen mit Hilfe des Remote-Lazy-Loading. Ich finde diese Vorgehensweise einfach und intuitiv. Dass das auf dem ersten Blick ein Performance-Problem in sich birgt, ist klar. Die Performance würde ich aber in einem 2. Schritt transparent durch einen Aspekt erhöhen, der preämptives Laden der im Anwendungsfall benötigten Objekte für einzelne Methoden realisiert in Kombination mit einem Cache. Das berührt weder den Business-Logik-Code noch die Objektgraph-Navigation sondern ist eine transparente Optimierung. Aber wie gesagt: dafür wurde ich keine Hibernate-Objekte verwenden mit angepassten Proxies...


----------



## oopexpert (7. Sep 2011)

> Mal ehrlich, den Obejktgraphen nach dem Commit zu validieren ist ja wohl ziemlich das idiotischte was man machen kann, das ist auch kein Hibernate Problem...


Die Validierung erfolgt nicht nach dem COMMIT, sondern nach allen UPDATES, INSERTS und DELETE und VOR dem Commit, was es nur geringfügig besser macht


----------



## maki (7. Sep 2011)

oopexpert hat gesagt.:


> Die Validierung erfolgt nicht nach dem COMMIT, sondern nach allen UPDATES, INSERTS und DELETE und VOR dem Commit, was es nur geringfügig besser macht


Naja, zumindest bleiben dann wenigstens die DB Daten konsistent. (?)

Wie gesagt, denke nciht dass ihr ein Hibernate/ORM problem habt (aber ich hatte nie den Eindruck als ob du das so schildern würdest ), ist eher ein menschliches/organisatorisches Problem mit einem ganz starken Einfluss von fehlender Kompentenz (u.a. auch in den verwendeteten technologien, aber nicht deinetwegen)...

Manchmal kann man Dinge nicht besser machen sondern muss einsehen dass man entweder geht, oder weitermacht aber mit weniger Motivation und Interesse am Projekt und den Kollegen.

1. You can't win
2. You can't break even
3. You can't even quit the game...


----------



## oopexpert (7. Sep 2011)

> Wie gesagt, denke nciht dass ihr ein Hibernate/ORM problem habt (aber ich hatte nie den Eindruck als ob du das so schildern würdest ) ...


Ich persönlich bin nicht unbedingt ein Hibernate-Freund insbesondere nicht, wenn es um XML-Mappings geht wegen dem besagten Medienbruch. Die Annotations sind da ein Kompromiss, mit dem ich einigermaßen leben kann. Ich versuche es zu vermeiden, den letzten Spezialfall aus Hibernate herauszukitzeln. ORM ist an sich Übel, Hibernate macht es bei richtiger Anwendung ein klein bischen erträglicher. Das sind zumindest die Erfahrungen die ich gemacht habe.
Im allgemeinen kann ich dem nur zustimmen, dass es kein Problem mit Hibernate an sich gibt, sondern nur mit dessen Verwendung in diesem Projekt. Das Verbiegen des Frameworks, wie es Effad treffend ausdrückte, bereitet mir Bauchschmerzen.


----------



## nocturne (8. Sep 2011)

Diese Diskussion wird gerade eine art Technologie-Review, deswegen habe ich nix mehr geschrieben.

Das wird insbesondere schwierig weil oopexpert vermutlich eine SSV unterzeichnet hat  - und hier über die Belegschaft geklatscht wird.

Eure Diskussion in Verbindung mit den Auftraggeberbetrieb von oopexpert lehne ich ab und würde einen neuen Thread begrüßen.


----------

