# Spring Boot mit Rest, JPA und Frontend



## lam_tr (13. Mrz 2018)

Hallo zusammen,

ich habe Spring Boot mit JPA und Rest mal angeschaut, finds auch richtig cool. Mir fehlt allgemein nur der Faden wie ich das aufteilen soll, sodass meine Anwendung nicht monolytisch wird.

Nehmen wir ein Adressbuch als Beispiel.

Ich habe vor den Server Teil in ein Spring Boot Projekt zu Packen mit Rest und JPA. An der Stelle gibt es folgende Komponente:
- RestPersonService // Rest Schnittstelle
- PersonRepository // Schnittstelle zur Datenbank

Ein normales Java Projekt mit Model (Shared Projekt), das zum Beispiel in der PersonRepository und Frontend (JavaFX) benutzt wird:
- Person

Zweites Spring Boot Projekt zum Darstellen der Oberfläche, das den Server Spring Boot Projekt und das Model Java Projekt im Classpath inkludiert.

Ist diese Vorgehensweise ok?
Ohne das Shared Projekt, müsste ich das Server Projekt direkt ansprechen um das Modell in der Oberfläche abbilden zu können.

Habt ihr da Tipps für mich.

Viele Grüße
lam


----------



## dzim (13. Mrz 2018)

Nutze Maven- oder Gradle-Module (bei Gradle nennt es sich "Projekte", glaub ich).
Ich hab auf Arbeit ein grösseres (Maven-)Projekt gegenwärtig so aufgeteilt:


Maven-Parent für einige festgelegt Versionen. Interessant für Projekte, die *nicht* explizit Spring Boot als Parent haben - also eigentlich nur das Modul "model" ... hat das Parent-Prinzip bei mir etwas ad-absurdum geführt, aber egal.
Hier die Module:

model - geteiltes Modell für *alle* Module, z.B. JSON-Pojos


db-service-postgres - JPA Entities für den DB-Zugriff auf Postgres
db-service-common - es gibt in dem Projekt zwei Server, einen intern ohne Zugriffsbeschränkung und einen externen mit Spring Security für Rollen-/Userbasierten Zugriff auf bestimmte Daten
db-service - interner ReST-Service
db-service-ui - externer ReST-Service (Kotlin! \o/ )


data-access - Schnittstelle für den Zugriff auf den "db-service"
controller, worker, validator - jeweils recht spezielle, durch "data-access" aber sehr schlanke Member eines eigenen Hazelcast-Clusters
Ein konkretes UI war vom speziellen Kunden nicht gewünscht, daher haben wir das nicht. Wir werden aber wohl eines für einfachere Admin-Zwecke bauen...
Das wird dann aber als separates "static" Web-Projekt erstellt (vermutlich mit Angular) und auf einem nginx, caddy, o.ä. deployed.

Man könnte aber auch "data-access" verwenden (oder es noch etwas aufspalten, da bei mir jetzt auch noch Cluster-Zeug enthalten ist), um eine JavaFX-Anwendung mit Spring Boot darüber an den Server zu verbinden (jedoch mit dem Risiko von Vollzugriff auf z.T. Daten, die man nicht von Menschen ändern lassen will). 
Auf alle Fälle kann man aber das "model" verwenden und könnte "data-access" als Vorlage nehmen, um den "db-service-ui" anzusprechen und damit sein Tool aufbauen. Könntest auch noch RxJava oder oder so in den Ring werfen.

Grundsätzlich ist also nur wichtig, dass du deinen Code in sinnvolle Module aufteilst, so dass du Zeug teilen/wiederverwenden kannst. Das beispielhafte Projekt hier ist schon etwas arg ausgeartet und ich würde dort kein weiteres UI-Projekt mehr reinstopfen, aber dann müsstest du halt so etwas wie "model" in ein separates Projekt auslagern. In deinem Fall aber würde es sicher noch gehen. Einfach ausprobieren!


----------



## looparda (13. Mrz 2018)

Ein paar Fragen aus Eigeninteresse, aber trotzdem nah am Thema:
@dzim
Liegen im _db-service_ Entities (mit ORM-Annotationen o.ä.) und im _model_ wirklich die Applikationsmodelle, auf denen die Business-Logik arbeitet? Wenn ja, wie macht ihr das Mapping zwischen diesen beiden Typen? Und benutzt ihr JPA/Hibernate oder ein anderes ORM?


----------



## dzim (13. Mrz 2018)

Im "db-service-postgres" sind die Entities enthalten. Im Nachhinein würde ich jedoch nicht mehr JPA verwenden sondern eher so etwas wie JOOQ, denn in speziellen Grenzfällen ist mir JPA noch etwas zu langsam (was auch sicher daran liegt, dass ich kein Profi damit bin). 
"model" enthält die DTOs, also die die Transferobjekte. Damit ein Mapping zwischen Entity und DTO so einfach wie möglich geht, habe ich mir da eine Klasse/API geschrieben, die Massiv auf Reflection setzt, um die Modelle zu überführen.
Sprich: innerhalb der "db-service-*" Module setze ich auf Entities, mit den "Consumern" (siehe "data-access" und die Cluster-Member) setze ich dagegen auf die DTOs.
Ist das ein guter Weg? Keine Ahnung! Er funktioniert gut, aber ist sicher etwas umständlich...

Man muss dazu sagen: Ich habe mehr oder weniger den gesamten Core und die Struktur des Projekts "verbrochen", ein Kollege von mir hat dann nur noch APIs in den "worker" und "validator" verwendet und muss nur in wenigen Ausnahmefällen sich in die Untiefen der anderen Module verirren. Er fand es aber praktisch, wie ich es aufgebaut hab.
Aber wie gesagt: Mit JPA bin ich dabei nicht warm geworden...
Was verwendest du?


----------



## looparda (13. Mrz 2018)

Ich benutzte Java mit Datenbank nur in einem Hobbyprojekt, welches aber durch folgendes eingeschlafen ist:
Ich habe Hibernate nicht auf die Reihe bekommen. Erst habe ich die Annotation-Verseuchten Entities in meiner JavaFX Applikation benutzt. Bei Beziehungen wurde es dann jedoch knifflig und ich bekam ständig Exceptions oder Probleme mit Objektgleichheit in Collections.
Später habe ich dann Beans in der JavaFX Applikation benutzt und sie per Hand zu den JPA-Entities gemappt. Eine einfache update Funktion war jedoch auch nicht machbar für mich.
Usecase:
Ein Projekt hat mehrere Personen. Personen können mehrere Adressen zugeordnet werden.
Jemand editiert über die GUI ein Projekt über Projekte > "Projekt A" > Personen > "Person A" > Adressen und
benennt Projekt zu "Projekt X", fügt eine Person hinzu, löscht eine weitere Person und bearbeitet die Adresse einer Person. Das Projekt-Objekt wird dann zur Datenhaltung weitergereicht an eine update-Funktion.
In der update-Funktion darf man sich dann im Mapping abrackern an allen CRUD-Kombinationen*beliebige Verschachtelungs-Tiefe (wurde das jetzt bearbeitet oder hinzugefügt? soll das jetzt gelöscht werden, weil es nicht mehr in der Bean Collection ist, aber noch in der Datenbank (und falls ja, sollen auch die Kinder gelöscht werden?(muss ich dafür das Repository des Typs des Kind-Entities bitten sich um die Löschung zu kümmern? (Wie komme ich dynamisch und Typsicher an das Repsoitory?)))?) und dann auch noch verschachtelt und generisch. Hab ich auch nicht hinbekommen und da sehe ich den Vorteil eines ORM nicht mehr. Hibernate sieht super toll aus, sofern man 1:n, n:m Beziehungen benutzt aber sobald man eine 1:nm:1 Beziehung mit Extra Attribut an der Beziehung hat wird es hässlich.
Ich denke das artet hier aber dann doch aus, wenn wir weiter Richtung Hibernate reden.


----------



## mrBrown (13. Mrz 2018)

Bisher war das gefühlt in jedem Projekt anders aufgeteilt 

Im aktuellsten siehts etwa so aus und klappt auch bisher ganz gut:

* parent
Projekt mit Build-Config und Abhängigkeiten, die alle brauchen, zB Tests etc.

* domain
Alles zur Domain gehörende.
Im wesentlichen Entitys (mit JAP, allerdings *nur* Annotationen), Services und Repository-Interfaces (an den Namenskonventionen von Spring orientiert, allerdings nicht die Spring-Repos erweiternd). Das ganze Projekt hat keinerlei Spring-Abhängigkeit. Im wesentlichen das M aus MVC 

* persistance
Enthält im wesentlichen nur Interfaces, die die Repo-Interfaces aus domain und das Spring-Repository-Interface extenden. Allerdings bin ich noch nicht sicher, wie viel Sinn das hat...

* rest & web
Enthält alle Controller, entweder als RestController oder geben entsprechende Views zurück. Nutze zT DTO etc, um die Domain-Objekte nach außen noch mal zu kapseln und ins passende Format zu bringen. Das Mapping zwischen Entity-DTO wird per Hand gemacht, DTOs sind aber recht flach und das ist damit nicht allzu kompliziert


----------



## mrBrown (13. Mrz 2018)

looparda hat gesagt.:


> Usecase:
> Ein Projekt hat mehrere Personen. Personen können mehrere Adressen zugeordnet werden.
> Jemand editiert über die GUI ein Projekt über Projekte > "Projekt A" > Personen > "Person A" > Adressen und
> benennt Projekt zu "Projekt X", fügt eine Person hinzu, löscht eine weitere Person und bearbeitet die Adresse einer Person. Das Projekt-Objekt wird dann zur Datenhaltung weitergereicht an eine update-Funktion.
> In der update-Funktion darf man sich dann im Mapping abrackern an allen CRUD-Kombinationen*beliebige Verschachtelungs-Tiefe (wurde das jetzt bearbeitet oder hinzugefügt? soll das jetzt gelöscht werden, weil es nicht mehr in der Bean Collection ist, aber noch in der Datenbank (und falls ja, sollen auch die Kinder gelöscht werden?(muss ich dafür das Repository des Typs des Kind-Entities bitten sich um die Löschung zu kümmern? (Wie komme ich dynamisch und Typsicher an das Repsoitory?)))?) und dann auch noch verschachtelt und generisch. Hab ich auch nicht hinbekommen und da sehe ich den Vorteil eines ORM nicht mehr. Hibernate sieht super toll aus, sofern man 1:n, n:m Beziehungen benutzt aber sobald man eine 1:nm:1 Beziehung mit Extra Attribut an der Beziehung hat wird es hässlich.


u.U. wäre etwas in Richtung Domain-Driven-Design einen Blick wert.

Solche Dinge finde ich persönlich mit JPA + Spring Data recht einfach zu lösen.

Aus dem Controller kommt das "Name ändern, Person hinzufügen, Person löschen"-Event. Projekt wird geladen (oder ist es schon), dieses führt die Änderungen durch, und danach wird  gespeichert. Irgendwelche Mappings per Hand etc würde ich vermeiden. Welches Repo zuständig ist, kann man nach DDD recht gut modellieren.


----------



## looparda (13. Mrz 2018)

Mich würde so eine Lösung sehr interessieren und ich würde JPA/Hibernate echt gern in den Griff bekommen. Können wir das vielleicht irgendwo besprechen? Ich habe ein Mini-Projekt, welches noch meine genannten Probleme beinhaltet aber man könnte dies vielleicht als Basis nehmen und zusammen umbauen.


----------



## mrBrown (13. Mrz 2018)

looparda hat gesagt.:


> Mich würde so eine Lösung sehr interessieren und ich würde JPA/Hibernate echt gern in den Griff bekommen. Können wir das vielleicht irgendwo besprechen? Ich habe ein Mini-Projekt, welches noch meine genannten Probleme beinhaltet aber man könnte dies vielleicht als Basis nehmen und zusammen umbauen.


liegt's bei Github oder so? Gern auch n eigener Post dazu


----------



## dzim (14. Mrz 2018)

Also wie ich schon sagte: Ich werde auch nicht zu 100% warm mit JPA. Ich habe ein paar der abstruseren Dinge in den Griff bekommen (z.B. Sequenzen bei Postgres... Die nötigen Annotationen... Grausig!), an anderen aber bin ich gescheitert (bestimmte Projektionen), so dass ich am Ende einige recht komplexe Anfragen mit native Queries über den EntityManager selbst gebaut habe (komplexe und sehr variable Anfragen für bestimmte Filter, die sonst statt einem solchen Query viele verschiedene über die Entities bedeutet hätten.
Wie gesagt: Ich werde JPA/Hibernate für ein solches Projekt *nicht* noch einmal verwenden, sondern nur noch bei kleineren Sachen. Oder sonst einfach Spring Data für NoSQL (MongoDB, etc.) verwenden.

@mrBrown - deine Aufteilung klingt logisch und gut, eventuell kann ich euch ja mal eine etwas bereinigte Variante meines ModelConverters zukommen lassen, da muss ich aber erst mal meinen Chef fragen, ob das OK ist... Denn manuelles Mapping genügt zwar sicher, ist aber auch recht aufwendig zu warten, wenn man etwas umstellt, oder?


----------



## mrBrown (14. Mrz 2018)

dzim hat gesagt.:


> @mrBrown - deine Aufteilung klingt logisch und gut, eventuell kann ich euch ja mal eine etwas bereinigte Variante meines ModelConverters zukommen lassen, da muss ich aber erst mal meinen Chef fragen, ob das OK ist... Denn manuelles Mapping genügt zwar sicher, ist aber auch recht aufwendig zu warten, wenn man etwas umstellt, oder?


Es geht eigentlich - die Mappings sind recht flach (selten mehr als eine zwei Entitys tief) und DTO und Entity entsprechen sich nicht immer 1-zu-1. Ich bin mir nicht sicher, ob ein über Reflection laufendes Mapping nicht sogar problematischer wird.
Zum Beispiel, in der Entity gibts zwei LocalDates, in einem DTO wird aber nur die Period dazwischen gebraucht, in einem anderem nur das zweite Datum. Mit Reflection stell ich mir das recht schwer zu bewerkstelligen vor? So wären das nur ein, zwei im Builder oder Konstruktor versteckte Funktionsaufrufe.


Edit: Vllt wäre statt DTO eher Presentation-Model der passende Begriff


----------



## mrBrown (14. Mrz 2018)

mrBrown hat gesagt.:


> * domain
> 
> * persistance
> 
> * rest & web



Ganz vergessen; oftmals lassen sich auch diese drei noch sinnvoll Teilen. Im Prinzip die Package-Struktur auf Modul-Ebene hochziehen. Dürfte besonders mit Java 9 noch mal interessanter werden.

Vielleicht ganz interessant: The Clean Architecture und das Buch dazu: Clean Architecture


----------



## dzim (14. Mrz 2018)

DTO heisst doch schon Data Transfer Object - und genau das sind sie bei mir auch... 
Was deine speziellen Fragen zur Konversion von Entity zu DTO angeht. Jaaaa, davor stand ich auch: z.B. übertrage ich in einigen Fällen nur einen Status-Namen, der in der DB aus einer eigenen Tabelle kommt (ich brauche also vereinfacht so etwas wie *entity.status.name* -> *dto.status_name*). In andere Fällen wird es noch etwas komplexer. 

Die Utility-Klasse ist extrem generisch, daher ist das kaum ein Problem: Die Basis ist, dass Felder mit identischen Namen und Typ direkt konvertiert werden, es sei denn, sie stehen auf einer zu-ignorieren-Liste. Zudem habe ich mir  mit einem Property-Mapping geholfen (mappe *entity.status.name *nach *dto.status_name*), zum anderen gibt es noch das fortgeschrittene Feature ein Mapping von Properties auf Functions anzulegen (speziell dann nötig, wenn die Typen sich eben nicht 1:1 via Reflection umsetzen lassen).

Beispiel:

```
public UserDto convertUserEntity(UserEntity in) {
    if (in == null)
        return null;
    return new ModelConverter<>(UserEntity.class, UserDto.class)
            // fieldMapping
            .addFieldMapping("role.name", "role")
            // field converter
            .addFieldConverter("company", this::convertCompanyEntity)
            // convert
            .convert(in).orElse(null);
}

public CompanyDto convertCompanyEntity(CompanyEntity in) {
    if (in == null)
        return null;
    return new ModelConverter<>(CompanyEntity.class, CompanyDto.class)
            // ignores
            .setIgnoredInFields("users", "devices")
            // fieldMapping
            // field converter
            // .addFieldConverter("companyConfiguration", this::convertCompanyConfigurationEntity)
            // convert
            .convert(in).orElse(null);
}

public CompanyConfigurationDto convertCompanyConfigurationEntity(CompanyConfigurationEntity in) {
    if (in == null)
        return null;
    return new ModelConverter<>(CompanyConfigurationEntity.class, CompanyConfigurationDto.class)
            // ignores
            .setIgnoredInFields("company")
            // field mapping
            // field converter
            // convert
            .convert(in).orElse(null);
}
```


----------



## mrBrown (14. Mrz 2018)

dzim hat gesagt.:


> DTO heisst doch schon Data Transfer Object - und genau das sind sie bei mir auch...


Bei mir würde ich sie eher Presentation-Model o.ä. nennen, zumindest dienen die nicht dem Transfer sondern einfach nur dem Formatieren für die Darstellung 



dzim hat gesagt.:


> Was deine speziellen Fragen zur Konversion von Entity zu DTO angeht. Jaaaa, davor stand ich auch: z.B. übertrage ich in einigen Fällen nur einen Status-Namen, der in der DB aus einer eigenen Tabelle kommt (ich brauche also vereinfacht so etwas wie *entity.status.name* -> *dto.status_name*). In andere Fällen wird es noch etwas komplexer.
> 
> Die Utility-Klasse ist extrem generisch, daher ist das kaum ein Problem: Die Basis ist, dass Felder mit identischen Namen und Typ direkt konvertiert werden, es sei denn, sie stehen auf einer zu-ignorieren-Liste. Zudem habe ich mir mit einem Property-Mapping geholfen (mappe *entity.status.name *nach *dto.status_name*), zum anderen gibt es noch das fortgeschrittene Feature ein Mapping von Properties auf Functions anzulegen (speziell dann nötig, wenn die Typen sich eben nicht 1:1 via Reflection umsetzen lassen).


Dabei schrecken mich zumindest bzgl der Wartbarkeit die ganzen Strings ein bisschen ab, mit Lambdas und Methodenreferenzen gehts aber wieder 
Für sowas wären typsichere Referenzen auf Attribute ganz interessant  

Möglicherweise ginge da auch ein auf Annotations aufsetzender Ansatz, da muss ich mal drüber nachdenken...
Dann hätte man die Konfiguration wieder direkt in den jeweiligen Klassen und muss sie nicht zusätzlich mitschleppen


----------



## dzim (14. Mrz 2018)

Ja, das klingt auch gut, hatte aber dann doch nicht sooo viel Zeit. Manchmal kommt man dann doch halt nicht um Strings herum... Gerade im Reflection-Umfeld. Was dir bei Annotations auch passieren könnte, denn am Ende musst du sie ja auch verarbeiten. Gut man könnte mit De-/Serializer á la Jackson oder so an die Sache gehen... Hm... Muss ich mal schauen, was ich da machen kann! :-D


----------



## mrBrown (14. Mrz 2018)

Ja, das Problem mit Strings hat man so oder so - aber zumindest muss man mit sowas De-/Serializer-mäßiges mit Annotations nur eine Seite das Mappings so angeben, und die andere sind die annotieren Felder 
Vielleicht ist das auch völlig umpraktikabel, aber die Idee für den Ansatz kam mir grad so


----------

