Was heißt eigentlich "validieren"?

"Validierung" ist einer dieser Begriffe, die für mich ein rotes Tuch sind, weil expliziter Code zur Validierung in vielen Fällen ein Design Smell ist.

Warum ist das so? Weil die Plausibilitätsprüfungen für eine IBAN nach meiner Meinung nicht etwa in einen IBANValidator gehören, der an das Framework gekoppelt ist, und schon gar nicht in eine Konfigurationsdatei, sondern am besten als expliziter PHP-Code in eine Klasse IBAN.

Wenn jemand die Frage stellt, welche Regeln in der Anwendung für eine IBAN gelten, dann ist es für mich ein sehr geradliniges Vorgehen, im Code nach dem Bezeichner IBAN zu suchen und dort nachzuschauen. Zudem können wir eine unabhängige Klasse IBAN auch viel einfacher in einem anderen Projekt wiederverwenden.

Oft meinen wir eigentlich Geschäftsregeln, wenn wir von Validierung sprechen. Wenn beispielsweise sagen "wir akzeptieren nur deutsche IBANs", was wir ja weiter oben schon so angedeutet hatten, dann ist das eine Geschäftsregel und keine Regel zur Validierung. Eine IBAN ist ja nicht ungültig, nur weil das Bankkonto in Österreich ist. Wir akzeptieren sie vielleicht nicht, aber technisch gesehen gültig ist sie trotzdem. So etwas ist eine Geschäftsregel.

Genau deswegen vermeide ich es, von Validierung zu sprechen. Stattdessen sollten wir uns auf die Erstellung von Objekten fokussieren, die fachlich relevant sind. So wie IBAN eben.

Solche fachlichen Objekte müssen immer gültig sein und dürfen niemals ungültig werden. Genau deswegen werfen wir im Konstruktor eine Exception, wenn wir an IBAN etwas übergeben, das nach unserem Dafürhalten gar keine IBAN sein kann. Ja, das ist ein Wertobjekt.

Objekte müssen immer in einem gültigen Zustand sein.

Mit diesem Wertobjekt machen wir aber keine Aussage darüber, ob eine IBAN ein tatsächlich existierendes Bankkonto repräsentiert. Das kann uns vermutlich nur die Bank selbst sagen, und möglicherweise will sie das aus Gründen der Sicherheit nicht tun. Im besten Fall gibt es einen Online-Service, den wir nach einer IBAN fragen und der uns sagt, ob diese IBAN ein existierendes Konto ist oder nicht. Im schlechtesten Fall müssen wir einen Prozess etablieren, um das selbst zu prüfen.

Dazu können wir beispielsweise einen Cent auf das Konto überweisen. Wenn wir diese Überweisung zurückerhalten, dann existiert offenbar kein Konto mit dieser IBAN. Wenn wir nichts weiter hören, dann haben wir einen Cent (plus die Überweisungsgebühren) dafür ausgegeben, herauszufinden, ob ein bestimmtes Bankkonto existiert.

Das ist übrigens eine Annahme, die nicht für jede Bank gelten muss. Eine Bank könnte sich ja auch über den unerwarteten Geldsegen freuen und den Cent, dessen Ziel-Konto gar nicht existiert, in die Kaffeekasse der Angestellten buchen. Aber wir wollen es hier jetzt nicht übertreiben. Daher ignorieren wir für dieses Beispiel geflissentlich auch die Temporalität der Welt ("letzte Woche hat das Konto noch existiert ...").

Wir haben damit jetzt drei Klassen von IBANs:

  1. Irgendein String, der keine gültige IBAN sein kann
  2. Ein String, der wie eine gültige IBAN aussieht
  3. Eine IBAN, von der wir wissen, dass dazu ein Bankkonto existiert

Mit dem ersten Fall können und wollen wir nichts weiter anfangen, außer vielleicht irgendwo eine Fehlermeldung anzuzeigen, dass der Benutzer eine falsche IBAN angegeben hat. Das ist der Fall, bei dem wir im Konstruktor von IBAN eine Exception werfen, denn wir müssen ja verhindern, dass ein IBAN-Objekt einen ungültigen Zustand hat.

Das ist übrigens ein Grund dafür, dass wir Wertobjekte unveränderlich machen. Ein anfangs gültiger Wert bleibt immer gültig, wenn er unveränderlich ist. Über die Existenz des Bankkontos im Laufe der Zeit sagt das nicht wirklich sehr viel aus, aber das wollten wir ja hier ausklammern.

Im zweiten Fall würden wir um diesen String sozusagen ein Wertobjekt wickeln, das implizit die Information "mein Inhalt sieht gut aus" transportiert. Das ist ein echter Mehrwert gegenüber der Arbeit mit skalaren Daten, denn wenn wir einen skalaren String als Parameter herumreichen, dann können wir als Empfänger ja nur hoffen, dass jemand diesen String bereits erfolgreich validiert hat und er seitdem nicht mehr verändert wurde. Ein unveränderliches Wertobjekt kapselt den Wert selbst sowie die Information, dass die Validierung erfolgreich war. Ansonste hätten wir das Objekt dank einer Exception im Konstruktor ja gar nicht erst erzeugen können.

Objekte sind besser als skalare Daten, weil sie Plausibilität garantieren können.

Bleibt der dritte Fall. Wenn unser IBAN-Objekt semantisch aussagt, dass ein String nach einer gültigen IBAN aussieht, wie repräsentieren wir dann eine IBAN, von der wir zusätzlich wissen, dass dazu ein Bankkonto existiert?

Sollten wir dem Objekt IBAN einen Status geben? Das würde allerdings der Unveränderlichkeit von Wertobjekten widersprechen.

Außer vielleicht ...