Validierung
Die Validierung von Eingabeparametern, Zuständen von Objekten usw. ist ein wichtiges Mittel um unerwartete (System-)Zustände zu vermeiden, die wiederum Fehler nach sich ziehen können. Das Grundkonzept der Validierung besagt, dass Validierung kontextbezogen ist und in der Domänen- und Geschäftslogik stattfindet. Ergänzend dazu kommt die Eingabevalidierung in UI-Elementen. Kontextbezogene Validierung bedeutet, dass nicht allgemein gesagt werden kann, ob ein Objekt oder Aggregat gültig ist oder nicht, sondern lediglich, ob es gültig für die Nutzung in einem bestimmten Kontext ist. Dementsprechend gibt es kontextspezifische Validierungen.
Anmerkung: Im Standard wird die Validierung für alle Kontexte gleich gehalten, das Konzept geht aber in Anlehnung an Nilsson, J: Applying Domain-Driven Design and Patterns: With Examples in C# and .NET weiter. Die Validierung hinsichtlich vieler verschiedener Kontexte ist daher eher selten! Im Standard wird hinsichtlich der Persistierung validiert.
In einem verteilten Microservice-System reicht an vielen Stellen Eventual consistency als Konsistenzmodell aus. Das bedeutet, dass die Konsistenz letztendlich gegeben ist, aber nicht instantan. Genau hier kommt die kontextspezifische Validierung ins Spiel, mit der sichergestellt werden kann, dass eine gewisse Funktionalität ausgeführt werden kann oder nicht.
Ein anderes Beispiel ist die Erfassung eines Buchs, wobei der Benutzer jegliche Zwischenstände speichern möchte. Das Buch muss in diesem Fall nur gültig hinsichtlich der Persistenz sein, nicht aber sofort für die Nutzung in allen Geschäftsprozessen. Entsprechend gibt es im Aggregate eine Methode ValidateRegardingPersistence() : ValidationResult
Das Ziel ist es hier natürlich, dass am Ende das Buch für möglichst alle Geschäftsprozesse gültig ist, was im Allgemeinen allerdings schwierig ist, weil beispielweise die Erfassung durch einen anderen Benutzer gereviewed und dann freigegeben werden muss. Zunächst möchte man aber vielleicht, dass alle Zwischenzustände persistiert werden, das Objekt aber noch nicht ausgeliehen werden kann. Dafür kann mithilfe der Methode ValidateRegardingBuecherAusleihen() : ValidationResult
prüfen, ob das Aggregat für höherwertige Funktionalität verwendet werden kann.
Hieraus ergibt sich das Konzept der frühen und späten Validierung. Frühe Validierung bedeutet, dass stets aus Nutzungssicht beauskunftet werden kann, ob der aktuelle Datenbestand valide ist hinsichtlich eines bestimmten (Ausführungs-)Kontexts.
Beispiel: Das Buch ist gültig hinsichtlich des Ausleihens - das Buch kann also ausgeliehen werden. Diese Prüfung kann vor der Ausführung der eigentlichen Funktionalität ausgeführt werden, in dem die frühe Validierung verwendet wird. Frühe Validierung versucht also die Wahrscheinlichkeit von Ausnahmen zu minimieren.
Späte Validierung meint, dass bei Ausführung einer Funktionalität mit aktuell ungültigem Datenbestand hinsichtlich eines (Ausführungs-)Kontexts eine Ausnahme geworfen wird. Bei Betreten eines Ausführungskontexts und erfolgreicher Validierung ist die Ausführung möglich und es wird kein Fehler geworfen.
Validierungsmeldungen bei Schleupen.CS müssen sich immer mindestens auf ein Aggregat (= das Pendant einer Business Entity aus Serviceebene) beziehen, um den Fehler über ein UI, Powershell-Cmdlet oder Entity Service beheben zu können.
Validierungsmeldungen
Validierungsmeldungen beinhalten immer einen Bezug zur Business Entity, damit der Benutzer oder auch Code geeignet auf Fehler oder Warnungen reagieren kann. Z.B. kann ein Bearbeitungsdialog geöffnet werden, in dem die entsprechende Entität geladen ist, damit ein Anwender das Problem direkt beheben kann.
Für eine Business Entity kann es mehrere Validierungsmeldungen geben. Jede Validierungsmeldung hat einen Text und eine Kategorie. Der Text muss den Anwender deutlich darauf hinweisen, was das Problem ist. Fehlermeldungen sollten also aussagekräftig sein, den fachlichen Fehler samt Ursache beschreiben und genug Detailinformation zur Behebung beinhalten. Insbesondere sind hier fachliche Schlüssel zu verwenden (z.B. der Titel eines Buchs).
Es existieren drei Kategorien von Validierungsmeldungen:
- Error
- Warning
- Info
Error bedeutet, dass die Business Entity (eigentlich das unterliegende Aggregat) im Kontext ungültig ist; wenn man trotzdem versucht die Aktion auszuführen, wird eine Ausnahme geworfen. Bei früher Validierung mit einem Error im Ergebnis, könnte z.B. verhindert werden, dass in einem Dialogablauf zum nächsten Dialogschritt navigiert werden kann.
Warning und Info könnten interessant für den Anwender sein, die Aktion kann aber dennoch durchgeführt werden. Hier ist dann fachlich zu entscheiden, ob diese Meldungen im UI oder in Protokollen angezeigt werden.
In der folgenden Abbildung ist ein Beispiel der Validierungsmeldung in der Domäne exemplarisch in einer Vollausbaustufe dargestellt.
Hierbei wird in der ValidationMessage auch das AggregateRoot implizit durch BuchValidationMessage sichtbar, da diese für die Validierung verantwortlich ist.
Anmerkung: Die speziellen Validierungsmeldungen sind nicht per Vererbung modelliert, da hierdurch die nutzende Schnittstelle bei Erweiterung inkompatibel werden würde. Das ist bei optionalen Assoziationen nicht der Fall!
Um die Kompatibilität von Services einfacher sicherzustellen, setzt das Servicedesign auf optionalen Assoziationen auf.
Validierung in Services
In der Service-Fassade eines synchronen Service wird nur implizit über die WCF validiert. Die eigentliche Validierung findet grundsätzlich nur in der Domänenlogik statt (Ausnahme: Eingabevalidierung), wobei die Fehler über einen ValidationException
gemeldet werden. Validierungsfehler im Falle der späten Validierung werden durch einen ValidationFaultContract
ausgeprägt, der ein ValidationResult
wie in obiger Abbildung beinhaltet.
Services bieten häufig an, früh zu validieren, dann wird das ValidationResult
direkt erstellt. Die Signatur hat dabei folgendes Format:
Validate[Regarding<Fachlicher Kontext>]Response Validate[Regarding<Fachlicher Kontext>](Validate[Regarding<Fachlicher Kontext>]Request request);
Beispiele:
ValidateResponse Validate(ValidateRequest request)
In diesem Fall wird hinsichtlich der Persistenz validiert.
ValidateRegardingBuecherAusleihenResponse ValidateRegardingBuecherAusleihen(ValidateRegardingBuecherAusleihenRequest request)
In diesem Fall wird hinsichtlich der Möglichkeit Bücher auszuleihen, validiert
Validierung in asynchronen Services
In aus Client-Sicht asynchron aufgerufenen Services (Command Service, Process Service, Request Reply Service) kann auch eine Validate-Operation definiert werden. Die Rückantwort kann dann über ein Event (Command Service, Process Service) oder eine geeignete Antwort (Request / Reply) erfolgen.
Das folgende Diagramm zeigt ein Beispiel und die Zusammenhänge der einzelnen Schnittstellendefinitionen:
In Wirklichkeit werden die Typen ValidationResult
, ValidationMessage
, ... nicht service-übergreifend wiederverwendet! Die Darstellung ist hier daher vereinfacht, um ein grundlegendes Verständnis zu erhalten.