Data Transfer Objekte

Effizienter Datentransport  – Sicherung kritischer Informationen

Die Verwendung von Datentransferobjekten hat zwei Ziele:

  • Informationen in einer für die Verwendung und den Netzwerktransport optimierten Weise bereitzustellen;
  • den Transfer möglicherweise sicherheitsrelevanter interner Informationen über Systemgrenzen (ein- und ausgehend) zu unterbinden.

Wikipedia gibt dazu folgende Definition:

Das Transferobjekt oder Datentransferobjekt (Abkürzung DTO) ist ein Entwurfsmuster aus dem Bereich der Softwareentwicklung. Es bündelt mehrere Daten in einem Objekt, sodass sie durch einen einzigen Programmaufruf übertragen werden können. Transferobjekte werden in verteilten Systemen eingesetzt, um mehrere zeitintensive Fernzugriffe durch einen einzigen zu ersetzen.

Implementierung
Für die Attribute von Transferobjekten steht nur eine begrenzte Auswahl an Datentypen zur Verfügung. Diese besteht aus primitiven Datentypen, einfachen Klassen und anderen Transferobjekten. Zusätzlich muss darauf geachtet werden, dass keine komplizierten Beziehungen zwischen den Transferobjekten entstehen. Empfehlenswert ist eine einfache Hierarchie, deren Klassendiagramm ein Baum ist.

Dabei wird der Transports über Netze in den Vordergrund gestellt. In Java bedeutet dies, dass DTO Objekte serialisierbar sein müssen. Der Aspekt der Sicherheit wird dabei nicht betrachtet. Sehr viel umfassender und auf die JEE Anwendungsarchitektur fokussiert werden Transferobjekte bei den Core JEE Patterns beschrieben. Teilweise ist dies auch auf Spring Applikationen übertragbar, speziell wenn andere View-Technologien als Vaadin verwendet werden.

Einsatz von DTOs in der Spring-Vaadin-Groovy-Architektur

Im Beispielprogramm werden DTOs als Datenschnittstelle zwischen dem View-Layer und dem Service-Layer verwendet. Auf den ersten Blick fällt dabei der Aspekt des Netzwerk-Transports weg, da die Vaadin-GUI mit Java-Klassen auf dem Server implementiert wird. Die Kommunikation mit dem in JavaScript geschriebenen Client im Browser wird automatisch durch Vaadin realisiert. Da aber dieser Datenaustausch weitgehend außerhalb der Kontrolle des Java-Programmierers abläuft, ist es auch hier besser, keine internen Informationen preiszugeben. Darüber hinaus kann die Vaadin-GUI potentiell auf mehrere Server verteilt werden, wenn die Applikation in großem Stil skalieren soll. Beschränkender Faktor ist die intensivere Kommunikation zwischen JavaScript-Client und Java Backend. Nach Angaben der Vaadin-Entwickler kann ein Vaadin-Server ca. 9000 simultane Benutzer bedienen, so dass bei sehr erfolgreichen Projekten die GUI-Schicht verteilt werden muss. Dann ergibt sich wieder eine Netzwerkkommunikation zwischen Service und GUI, die leichter angreifbar ist als eine Server-interne Kommunikation.

Primär werden die DTOs aber verwendet, um geeignet aufbereitete Daten für die Benutzerschnittstelle zur Verfügung zu stellen. Diese Daten sind oft stark denormalisiert, enthalten Informationen mehrerer Domain Objekte und orientieren sich an den Erfordernissen einzelner Use Cases oder Benutzergruppen. Es ist dabei sinnvoll, zwischen ausgehenden (Service zur GUI) und eingehenden (GUI zum Service) DTOs zu unterscheiden. Mehr dazu im Kapitel über CQRS.

Bei ausgehenden DTOs hat sich eine Unterscheidung in List-DTOs und Full-DTOs bewährt:

  • List-DTOs enthalten für eine Menge von Objekten eindeutige Schlüssel (z.B. die Persistenz-ID) zusammen mit Strings zur Darstellung in UI-Elementen zur Objektauswahl. Dabei können Baumstrukturen oder einfache Listenstrukturen sinnvoll sein. Bei Auswahl eines angezeigten Elements in der GUI kann über den in der Regel nicht angezeigten Schlüssel die vollständige Information für das Element geholt werden.
  • Full-DTOs enthalten alle Informationen, die zur Anzeige in einer Detailansicht benötigt werden. Das können Informationen aus mehreren Domain-Klassen sein.

Die Anzahl der DTO-Klassen ist relativ groß. Daher bewährt es sich, diese Klassen pro Use Case (bzw. Gruppe von Use Cases) zu organisieren. Dafür können Packages oder Klassen mit inneren Klassen (wie im Beispiel) verwendet werden. Die Service-Schicht ist dafür zuständig, DTOs bereitzustellen bzw. als Eingabe entgegenzunehmen.

Beispiel

Die Klasse TaskDTO stellt DTOs für die Auswahl, Darstellung und Veränderung von Task-Objekten als innere Klassen bereit. Die Namen der Query-Klassen beginnen mit Q, der Command-Klassen mit C.

class TaskDto {
    public static class QList {
        LinkedHashMap<Long, String> all = [:]

        Long getFirstId() {
            if (all) {
                all.keySet().iterator().next()
            } else {
                0
            }
        }
    }

    public static class QFull {
        Long id
        String classname
        String tag
        String description
        Long estimate
        Long summedEstimate
        Long spent
        Boolean completed
        ProjectDto.QList project = new ProjectDto.QList()
        SprintDto.QList sprints = new SprintDto.QList()
        QList supertask = new QList()
        List<QNode> subtasks = []
    }

    public static class QNode {
        Long id
        String tag
        List<QNode> children = []
    }

    public static class CSet {
        Long id = 0
        String classname
        String tag
        String description
        Long estimate
        Long spent
        Boolean completed
        Long projectId
        List<Long> sprintIds = []
        Long supertaskId
        List<Long> subtaskIds = []
    }
}

Zeile 7
Die Klasse TaskDto.QList verwaltet IDs und Namen einer Menge von Tasks in einer LinkedHashMap
Zeile 19
Klasse TaskDto.QFull enthält nicht nur die Werte der Felder von Task, sondern auch List-DTOs mit den assoziierten Projekten und Sprints und der Supertask. Mit Hilfe der subtask-Variablen (Zeile 31) wird ein Baum untergeordneten Tasks bereitgestellt.
Zeile 34
TaskDto.QNode enthält eine rekursive Datenstruktur, die zur Implementierung der Taskbäume dient.
Zeile 40
Klasse TaskDto.CSet wird für Create und Update Operationen verwendet. Hier werden nur die Werte für ein einziges Objekt übertragen, allerdings mit den IDs aller assoziierten Objekte. Damit können die Assoziationen angelegt oder aktualisiert werden

Die anderen DTOs sind analog aufgebaut.

Aufgabe

Implementieren Sie DTOs für Ihre erweiterten Query- und Command-Modelle (s. Aufgabe bei CQRS).

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.