Spring REST Application (Data Retrive) API und Architectur hilfe

LokiA

Mitglied
Ich will gerade eine Web Anwendung schreiben – und wollte dazu zuerst noch euren Rat einholen.

Es ist eine REST Anwendung die eigentlich nur Daten aus einer Datenbank zur Verfügung stellt (Read only).
Das Problem ist, dass die Datenbank-Struktur schon vorgegeben ist (und das blöd – aber daran kann ich nichts ändern):

Entries
- ENTRY_ID
- USER
- IP
- DATETIME
- CATEGORY (ENUM)
- CATEGORY-SUB (ENUM)
- DESCRIPTION

In der Datenbank ist es so aufgeteilt (Unnötig aber nicht änderbar!):
SQL Table: ENTRIES_PART_1
- ENTRY_ID
- DATETIME
- CATEGORY (ENUM)
- IP

SQL Table: ENTRIES_PART_2
- ENTRY_ID
- USER
- CATEGORY-SUB (ENUM)
- DESCRIPTION

Folgende Rest-Endpoints solls geben:
/entries --> Einträge können abgerufen werden - mit Filterung (auf alles außer Description) & Paging.
/info -> so Sachen wie summen für diverse Kategorien heraus holen (wie oft in einem Zeitfenster eine Kategorie, …)

Eigentlich denke ich das das Ganze sehr einfach aufgebaut werden könnte, nachdem ich mir (u.a.) folgenden zwei Spring Guids angesehen habe:
https://spring.io/guides/gs/actuator-service/
https://spring.io/guides/gs/relational-data-access/

Sprich ich brauche eigentlich nur (mit Spring Boot):
Einen RestController (Class mit @Controller) der die Endpoints definiert.
Eine EntryJsonObj Klasse als Respone represenation (der dan auf JSON gemappt wird bei der Response)
Eine InfoJsonObj Klasse als Respone represenation (der dan auf JSON gemappt wird bei der Response) -> da muss ich noch überlegen, sollte aber auch einfach sein

Eine DBAccess Klasse die mit JDBCTemplate die Daten aus der Datenbank holt - die wird in den RestController Injected damit dieser die Daten abholen kann. Darin wird mit dem JDBC template einfach aus beiden Tabellen gejoined und fertig (eine Abfrage für das entries filtern/pagen + eine Abfrage für die anderen (da ggf mehr)).
Eine EntryDbObj Klasse als Transfer Object Zwischen DB und REST Controler dient für die Entries. (wird auf EntryJsonObj gemapped)
Eine InfoDbObj Klasse als Transfer Object Zwischen DB und REST Controler dient für die Info Response. (wird auf InfoJsonObj gemapped)

Dazu jetzt folgende Fragen:
1) Übersehe ich dabei etwas?

2) Mir kommt es recht einfach vor, eigentlich muss ich beim RestConroller nur alle Parameter an die DBAccess weitergeben, damit die auf der DB den Query absetzt und wieder zurück geben kann.
Eigentlich könnte man sich ja DBAccess und EntryDbObj sparen, aber von wegen Seperation of Concerns ...
Oder müsste/sollte ich da die Klassen weiter aufsplitten?

3) Wie sieht es mit dem Mapping von EntryDbObj auf EntryJsonObj aus, das könnte doch sicher auch irgendwie automatisiert oder vereinfacht werden?

4) Als ich begonnen habe und mir nch keine Überlegung gemacht habe wie ich die daten aus der DB holen kann, wollte ich im RestContoler mit Streams arbeiten ...
weil man damit sehr schön elegant und einfach alles erledigen kann:
Paging mit skip() und limit()
Filtering mit mit filter(e -> e.isCategory(X))
kann man das auch irgendwie auf die DBAccess anwenden - so das die einen Stream liefert?
(weil alles aus der DB holen geht ja wegen der Menge der Entries nicht (mehrere Millionen))
Sonst geht nur Paramter and DB durchschleifen ... oder gibt es alternativen

5) Zur Architektur - gibt es was was man verbessern ändern sollte?
Ich versuche mich gerade mit Architektur zu befassen, erkenne aber bei der Anwendung nicht die verschiedenen Schichten / Module
Also sowas wie MVC? Domain/Business Logic?
Oder ist die Anwendung nur einfach zu simpel damit man das nicht sehen/schöner machen kann. Die Entries (als Domain Objects) sind nun mal eigentlich dumme Value objects die nichts können (keine Logic) ....
 
Eigentlich denke ich das das Ganze sehr einfach aufgebaut werden könnte
Richtig. Hier geht es um eine simple Query. Das Ergebnis sind keine Objekte (weder Value Objects noch Domain Objects), sondern schlicht Daten. Diese sollen zu JSON transformiert werden und das war es auch schon. Dafür muss man keine zig Klassen schreiben.

aber von wegen Seperation of Concerns ...
Welche Concerns siehst Du denn?
 
Danke, für die Antwort.

Richtig. Hier geht es um eine simple Query. Das Ergebnis sind keine Objekte (weder Value Objects noch Domain Objects), sondern schlicht Daten. Diese sollen zu JSON transformiert werden und das war es auch schon. Dafür muss man keine zig Klassen schreiben.
Meinst du damit, dass die von mir erwähnten Klassen schion zu viel wären?
Oder wie macht man den das eben erwähnte mapping am elegantesten?


Welche Concerns siehst Du denn?
Naja zumindest:
- das handlen der der REST requests
- Zugriff auf die DB
- das mapping dazwischen
- ggf Brechung für den INFO endpoint (ob das wirklich was eigenes wäre muss ich mir noch ansehen)
BZW wie würdest du sonst den Durchgriff auf die Daten in der DB machen?
 
Im Prinzip brauchst du ja gar keine Klassen. Du weist ja auch gar nicht welche Klassen der Benutzer des Services erwartet. Also wirst Du die Daten aus der DB einfach in ein JSON Object packen und zurück geben. Um alles andere muss sich die Software kümmern die den REST Service benutzt.
 
@Thallius hat es sehr schön auf den Punkt gebracht.

Es geht doch einfach darum, eine SQL-Abfrage zu stellen und das Ergebnis als JSON formatiert auszugeben. Da gibt es weder ein Domain Model, noch gibt es Entity-Klassen. Im Extremfall (das ist keine Empfehlung) könntest Du alles in Deiner Service-Klasse erledigen.

Prinzipien/Patterns sind nicht dazu da, sie blind einzusetzen. Ein "Hello World" teilst Du doch auch nicht so auf: HelloWorldView, HelloWorldModel und natürlich HelloWorldController. Dann natürlich einen Greeter für die Business Logic und eine Domain-Klasse für den Namen, lass mal überlegen, ach ja ein Value Object. Noch ein paar Interfaces (design by contract) und ein DI-Framework und schon ist das System.out.println("Hello " + name); fertig.
 
Prinzipien/Patterns sind nicht dazu da, sie blind einzusetzen. Ein "Hello World" teilst Du doch auch nicht so auf: HelloWorldView, HelloWorldModel und natürlich HelloWorldController. Dann natürlich einen Greeter für die Business Logic und eine Domain-Klasse für den Namen, lass mal überlegen, ach ja ein Value Object. Noch ein paar Interfaces (design by contract) und ein DI-Framework und schon ist das System.out.println("Hello " + name); fertig.
https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition 😎
 
Also auf Klassen zu verzichten halte ich wohl für ziemlich übertrieben - Seperation of Concerns / SRP gilt ja wohl auch hier. Daher finde ich eure aussagen wohl wissend, dass es klare Übertreibungen sind nicht richtig.
Da ich aber dennoch euren Rat als immer wieder le[h/e]rreich empfinde wollte ich nochmal nach-harken - wir würdet ihr sonst das mapping DB-Object JSON-Response-Object machen?
Manuell oder anders?
 
Also auf Klassen zu verzichten halte ich wohl für ziemlich übertrieben - Seperation of Concerns / SRP gilt ja wohl auch hier. Daher finde ich eure aussagen wohl wissend, dass es klare Übertreibungen sind nicht richtig.
Da ich aber dennoch euren Rat als immer wieder le[h/e]rreich empfinde wollte ich nochmal nach-harken - wir würdet ihr sonst das mapping DB-Object JSON-Response-Object machen?
Manuell oder anders?

Immer noch:

Gar nicht! Was für ein Java Objekt willst du denn erstellen wenn du es eh wieder in ein JSON object zerlegst? Das ist einfach nur sinnlos.
 
Also auf Klassen zu verzichten halte ich wohl für ziemlich übertrieben
Er meinte vermutlich Dinge wie Entity-Klassen.
Daher finde ich eure aussagen wohl wissend, dass es klare Übertreibungen sind nicht richtig.
Das sind keine Übertreibungen - das passiert, wenn man Prinzipien und Patterns als die über allem stehenden Gesetze versteht.

Vielleicht hilft es, wenn Du Dir mal für einen Moment vorstellst, Du hättest in der DB eine View, die den benötigten JSON-String liefert. Würdest Du dann auch hergehen den JSON-String erstmal in ein JSON-Object einlesen, dieses auf eine EntityDbObj-Instanz abbilden, dann mit einem Mapper auf eine EntityJsonObj-Instanz, dann zurück auf JSONObject und am Ende wieder auf den String, den Du am Anfang aus der DB erhalten hast?

Nein? Genau das versuchst Du aber, nur mit dem Unterschied, dass Du am Anfang eben keinen String sondern mehrere Werte erhältst.

Es geht hier nicht um Objekte, sondern um Daten und die Aufgabe besteht einfach darin, Daten zu formatieren (z. B. jede Zeile in JSONObject einlesen, das Ergebnis als JSONArray zurückgeben und dann als String ausgeben lassen).
 
Ach herrje - jetzt bin ich aber ganz schön lange auf dem Schlauch gestanden ...
Mein EntryDbObj ist das selbe wie EntryJsonObj daher brauch ich das nicht doppelt moppeln ... klar!!

Noch eine vielleicht letzte frage - wie würdet ihr die Daten aus der DB auslesen?
Bei so einer einfachen und schon vorgegebenen Struktur - JDBCTemplate oder JPA?
 
Mein EntryDbObj ist das selbe wie EntryJsonObj daher brauch ich das nicht doppelt moppeln ... klar!!
Und EntryDbObj/EntryJsonObj machen das gleiche wie JsonObject, daher brauchst Du "keine" Klassen.

JdbcTemplate kenn ich zwar nicht (mach kein Spring), sieht aber gut aus. Je nachdem, was Du vorhast, kann ein RowMapper<JsonObject> oder RowCallbackHandler (beides funktionale Interfaces) verwendet werden, um eine List<JsonObject> oder direkt ein JsonArray zu füllen.

Java:
public JsonArray find(...) {
    JsonArrayBuilder rows = Json.createArrayBuilder();
    RowCallbackHandler callback = rs -> {
        rows.add(Json.createObjectBuilder()
            .add("id", rs.getLong(1))
            .add("user", rs.getString(2))
            ...
        );
    };
    template.query(..., callback);
    return rows.build();
}
 

Zurück
Oben