Modellanbindung Vaadin Benutzeroberfläche

Artikel in der Java-Aktuell Themenschwerpunkt Architektur

Die Ausgabe 02/2022 enthält einen Artikel mit einem Anwendungsszenario für eine Cloud-basierte Software Architektur. Das Spring-Beispiel mit Cloud-Support und DataProvider Anbindung zur REST Schnittstelle stellt das komplexe Zusammenspiel von mehreren separaten Diensten dar, im Artikel wird auf die Hintergründe zu Design-Entscheidungen eingegangen und die eingesetzten Patterns bei der Software-Entwicklung beschrieben. ‘Wartbare, zukunftsfähige Software-Architekturen in Java mit Annotation-Processing, Spring Boot und Cloud-Technologien‘, ab Seite 33.

Screenshot der GUI Anwendung, die eine REST Schnittstelle in die Clout nutzt

Das beschriebene Beispiel (verfügbar auf GitHub) besteht aus einem Maven Projekt mit mehreren Modulen, eines enthält die Bibliothek mit dem Datenmodell, eines ist eine Spring-Applikation mit Vaadin Web-Benutzerschnittstelle und zuletzt ist ein Spring REST Dienst implementiert, der die Daten bereitstellt und von der GUI genutzt wird. Das Projekt zeigt auch wie ein REST-Query mit Feld-Sortierung und -Filterung durchgeführt wird und die entsprechende FeignClient Definition aussehen kann.

Modul DataModel

Dieses Modul enthält das Datenmodell definiert die Entitäten die von UserInterface und RestServiceEndpoint verwendet werden. Das Projekt enthält auch Value-Objects mit Attributen für Filtern und Suchen (TripFilterVO). Der Benutzer gibt die Filterkriterien in der Oberfläche ein und diese werden weiter über die REST Schnittstelle an den Microservice geschickt. Es muss kein zusätzlicher Code geschrieben werden, da die VO’s automatisch aus den Entities generiert werden.

Modul UserInterface mit Cloud-Anbindung

Enthält den Programmcode der Benutzeroberfläche mit einer Liste und einer Eingabemaske zum editieren. Der meiste Code zur Darstellung der Komponenten wird aus den Informationen am Datenmodell erzeugt.

Start über die Kommandozeile mit mvn spring-boot:run

Das Projekt zeigt wie Paging mit einer REST Serviceschnittstelle implementiert werden kann und nutzt generierte Client-Requests  (z.B.com.uc_mobileapps.examples.restclient.TripClient). Der Code wird automatisch aus den Methoden im Controller ‘TripController’ erzeugt, der im RestServiceEndpoint Projekt definiert ist.

Wenn eine neue Service-Method hinzugefügt wird, aktualisiert das System den Client zur Übersetzungszeit.

Die Oberfläche der Anwendung ist über die Socket-Adresse  https://localhost:8080 erreichbar. Die Service-Discovery wird im Beispiel simuliert und mappt alle Anfragen auf  ‘localhost:8081’ (siehe unten).
Weiterführende Informationen über die Ordnerstruktur auf der englischen Seite.

Modul RestService

Dieser Dienst hört auf localhost:8081, es ist eine Spring Cloud Implementierung eines REST-Controllers. Die Datenbank-Anbindung mit Paging und Filter ist hier implementiert. Im Projekt ist keine Service-Registration konfiguriert (siehe Spring-Cloud Consul).

Start von der Kommandozeile mit mvn spring-boot:run

Das Modell wird nach SeifeModels/RestService.classmodel.json exportiert und zur Erstellung der Clients für den REST-Endpoint benutzt.

JPA Daten-UI Bindings für Vaadin

Die Erweiterung  der Übersetzungsphase durch einen Annotation-Processor eröffnet besonders für große Projekte neue Möglichkeiten. ‘Seife’ in der Version 1.8 unterstützt die direkte Anbindung von Benutzeroberflächen-Komponenten an Entities und andere Modellklassen. Unterstützt wird die automatische Erstellung der Bindings für Vaadin Forms und das Vaadin Grid.
Die Bindings können für jeden Datentyp angepasst werden, sei es für einen spezifische Modellklasse oder einen der Java-Basistypen. Das Beispiel zeigt ein ‘Customer’-Entity dessen Attribute im Formular oder als Liste dargestellt wird.

Beispiel herunterladen: VaadinSpringFields-1.8.0

Modell, UI-Code und Template werden transformiert
Funktionsprinzip Code-Templates mit Seife: Modell, UI-Code und Template werden transformiert

Die Anbindung des Grid wird direkt vom Datenmodell abgeleitet. Im Gegensatz zur herkömmlichen Art sind jedoch Code-Vorlagen für bestimmte Datentypen oder Datenstrukturen möglich die die am Entity festgelegten Constraints berücksichtigen. So wird das gleiche Template an allen Stellen genutzt.

@SeifeForm(forClass = Customer.class, generatorOptions = {"grid.gridClass"})
public class CustomerGrid extends Grid<Customer> {

    /**
     * Empfohlen wird die Felder gleich zu den Attributnamen des Entities zu
     * benennen. Die Felder werden dann implizit angebunden.
     */
    @SeifeBinding
    private Column<Customer> firstName;

    /**
     * Wenn nötig kann der Name explizit angegeben werden. Auch eine Punkt-
     * Notation zum Navigieren im Objektgraph ist möglich.
     */
    @SeifeBinding("lastName")
    private Column<Customer> name;
// [..]

Die Konversion des Boolean-Datentyps ist mit Vaadin-Bordmitteln nicht ohne zusätzlichen Code zu bewerkstelligen, dies wird vom Seife-Framework zur Übersetzungszeit automatisch übernommen:

    favoriteProductName = addColumn(entity -> Optional.ofNullable(entity.getFavoriteProduct()).map(p -> p.
      getName())
      .orElse(null));
    favoriteProductName.setKey(COLUMN_FAVORITE_PRODUCT_NAME);
    favoriteProductName.setId("favoriteProductName");
    premium = addComponentColumn(entity -> {
        Checkbox cb = new Checkbox(entity.isPremium());
        cb.setEnabled(false);
        return cb;
      });
    premium.setKey(COLUMN_PREMIUM);
    premium.setId("premium");

Es wird beispielsweise automatisch eine Checkbox für alle Spalten vom Typ Boolean von einer Code-Vorlage erstellt. Seife bringt einige dieser Vorlagen mit, sie können aber auch direkt im Projekt mit gepflegt werden und sind somit auch immer passend versioniert.

Gleiches ist für Formulare möglich.

Formular, das deklarativ an das Datenmodell angebunden wurde.

In einem solchen Eingabeformular werden zudem automatisch eindeutige IDs für die Controls erzeugt die für Integrationstests nutzbar sind.

Weitere Informationen in der Dokumentation (engl.): Documentation