Programmierung
•
Dec 15, 2022

Mateusz Jarosz
Senior Backend-Entwickler
Ein kurzer Einblick in die beliebteste dry-rb-Bibliothek: Ersetzen von ActiveRecord-Modellvalidierungen durch dry-validation, dry-schema und dry-types-Gems.
{Wir schreiben das Jahr 2023, Ruby wurde vor 26 Jahren veröffentlicht, Rails 9 Jahre später. Zu sagen, dass Rails das Ruby-Web-Ökosystem dominiert hat, ist noch zu wenig. Webanwendungen ohne großen Aufwand so schnell wie möglich mit der Einfachheit eines Hammers zu schreiben? Verdammt ja, meldet mich an! Das Design von Rails erschien brillant und fühlte sich an wie der Heilige Gral der Webentwicklung. Die Konventionen machten es uns so bequem, dass wir überhaupt nicht nachdenken mussten. Alles, was wir tun mussten, war, uns von Rails an die Hand nehmen und durchs Leben führen zu lassen.
Aber seit 18 Jahren ist das Framework auf dem Markt, und es hat sich gezeigt, dass seine Einfachheit für komplexere Anwendungen nicht ausreicht. Der ultimative Schuldige von Rails muss sein Engagement für das MVC-Designmuster sein. Ja, das ist ein sehr guter Grund, warum es Rails gelungen ist, über die Jahre hinweg einfach zu bleiben und seine Fähigkeit zu bewahren, neue Entwickler anzulocken. Für Anfänger und kleine Projekte ist es immer noch eine gute Wahl.
Für größere Plattformen ist MVC jedoch bei weitem nicht genug. Es ist ein Schlaraffenland für Probleme, wenn Ihre Anwendung schnell wächst. Plötzlich wendet sich fast jeder Vorteil, den Rails bei der Programmierung einfacherer Anwendungen bietet, gegen uns. Jeder Mechanismus, der einfach zu benutzen ist, wird überwuchert, überstrapaziert und extrem schwierig zu warten. Es ist leicht, die Kontrolle zu verlieren und Fear-Driven Development® zu praktizieren. Rails skaliert nicht, und das ist eine Tatsache.
Hier betritt dry-rb den Raum! Ganz in Weiß. dry-rb ist ein Satz von Bibliotheken, die nur mit einigen Root-Komponenten gekoppelt sind und getrennt vom Rest des Pakets verwendet werden können. Es ist eher ein Toolset als ein Framework. Es wird nicht mit einem Webserver ausgeliefert, es hat keine Projektarchitektur und es ist nicht darauf ausgelegt, Webanfragen selbst zu bearbeiten. Es ist also eher eine Ergänzung als eine fertige Lösung, aber eine sehr wichtige. Diejenige, die die meisten Unzulänglichkeiten von Rails beheben könnte.
Der erste Schritt
Also! Obwohl dry-rb viele interessante Konzepte und Komponenten enthält, wollen wir es Schritt für Schritt angehen und mit der einfachsten und am häufigsten verwendeten Bibliothek beginnen. Wir sprechen von dry-validation.
Betrachten wir also dieses Beispiel:
Was ist daran falsch?
Well. Es kommt darauf an. Wenn es sich um eine Universitätsarbeit oder eine Hobby-App handelt, ist es in Ordnung, es kann so bleiben, da es einfach die Anforderungen und Erwartungen erfüllt.
Aber was ist, wenn wir eine super komplexe Anwendung mit Dutzenden von Modellen mit komplizierten Verknüpfungen zwischen ihnen entwerfen? Und mit bedingten Validierungen. Das kann langsam aus dem Ruder laufen. Es wird immer schwieriger, diese Art von Validierungen und diese Art von Design zu pflegen. Bitte beachten Sie, dass das Beispiel nur Validierungen zeigt. Den Konventionen von Rails folgend, gäbe es Assoziationen, Callbacks, Scopes, Attribut-Accessors, Methoden und all den anderen Kram. Sehen Sie, wie es immer schwieriger wird, den Code zu pflegen?
Ok, aber mit etwas Mühe ist es immer noch möglich, den Code in einem einigermaßen guten Zustand zu halten. Wachsende Komplexität ist nicht gerade ein ActiveRecord-Validierungskiller. Stellen wir uns also folgendes vor: Was ist, wenn wir mit Microservices mit mehreren Datenbanken arbeiten? Und wir müssen eine Änderung über viele Anwendungen mit Ereignissen weitergeben? Kopieren wir dieselben Validierungen für das gesamte System? Extrahieren wir Modelle in eine Art gemeinsame Bibliothek? Beide vorgenannten Lösungen stehen im direkten Widerspruch zur Grundidee von Microservices. Das tun wir hier nicht.
Wie gehen wir also vor?
Was, wenn wir Datenvalidierung und Persistenz entkoppeln? Wie wäre es, wenn wir die Daten zuerst validieren und die Daten erst nach der Validierung an die Persistenzschicht weitergeben? Auf diese Weise können wir auch sehr früh im Prozess der Bearbeitung eingehender Anfragen Validierungen durchführen. Schließlich sollten Validierungsfehler so schnell wie möglich zurückgegeben werden, um zu verhindern, dass anderer Code unnötig ausgeführt wird. Um dies zu erreichen, müssen wir über die Datenvalidierung auf die gleiche Weise nachdenken wie über Authentifizierung und Autorisierung. Sie ist nur ein weiterer Schritt im Ablauf der Anfrageverarbeitung. Wir führen sie an einem bestimmten Punkt bei der Verarbeitung der Anfrage durch und geben nur die validierten Daten weiter. Und wir tun es einmal, genau dann, wenn wir die Eingabe erhalten, und nicht jedes Mal, wenn das Modell berührt wird. Klingt gut?
Ok, los geht's!
Das Makeover
Wie im vorherigen Abschnitt erwähnt, möchten wir die Validierung anfragespezifisch und nicht modellspezifisch machen. Wir werden Validierungen pro Anfrage durchführen, nicht pro Modellspeicherung. Daher werden wir Namen verwenden, die sich auf unsere Endpunkte und nicht auf Modelle beziehen. Und so könnte SignUpContract
aussehen:
Ja, richtig, aber es gibt einige skizzenhafte Dinge, die hier vor sich gehen. Wo ist das Modul Types
? Und was macht es? Sie haben es erraten - wir haben eine weitere Bibliothek dafür! dry-validation verwendet dry-types unter der Haube, um erwartete Eingaben zu definieren. Die Bibliothek führt ein ordentliches Typisierungssystem in Ruby ein. Sie können mehr darüber erfahren hier. Die Typen von ResourceID
sind jedoch nicht in den dry-types enthalten, Email
, or Password
. Wir werden sie selbst definieren, indem wir den Standard-Typensatz erweitern.
Es ist wichtig, hier das Konzept eines Typs zu verstehen. Die klassenähnlichen Objekte im Modul sind in Wirklichkeit Validatoren, die mit der dry-types DSL erstellt wurden. Anstatt die gleichen Validierungsregeln für verschiedene Verträge immer wieder zu wiederholen, können wir einen Typ und seine Einschränkungen in einem Modul zusammenfassen und überall wiederverwenden. Auf diese Weise schaffen wir einen vollwertigen Datentyp. Von nun an können wir behaupten, dass ein bestimmter Wert eine E-Mail oder ein Passwort sein sollte und nicht nur ein String mit dem richtigen Format. Nette Abstraktionsschicht, wenn Sie mich fragen.
Ok, kommen wir zurück zu unserem Modell:
Hübsch, nicht wahr? Und so könnte Ihre Controller-Aktion aussehen:
Der obige Code ist lediglich ein Beispiel dafür, wie Verträge mit Rails verwendet werden könnten. Die Idee dahinter ist, einfach die grundsätzliche Verwendung zu zeigen, anstatt eine fertige Lösung anzubieten. Es hat 0 Kaffees gekostet, ihn zu schreiben, also möchten wir alle Umgehungswilligen ermutigen, ihre eigenen Implementierungen zu erstellen. Sorry, copy-paste Enthusiasten.
Nun, da wir all die Schönheiten von anfragespezifischen Validierungen kennen, lasst uns ein wenig mürrisch sein und über einige Nachteile sprechen. Nichts ist wirklich perfekt und es ist gut, sowohl die Vor- als auch die Nachteile der Lösungen zu kennen, die wir zu implementieren gedenken. Der wichtigste Nachteil dieses Ansatzes ist die schwierigere Gewährleistung der Datenintegrität im Allgemeinen. Es ist einfacher, dies mit Modellen zu erreichen, da Modelle die Daten bei jedem Persistenzaufruf validieren (es sei denn, wir weisen sie ausdrücklich an, dies nicht zu tun). In diesem Fall müssen wir entweder sehr vorsichtig mit unseren Validierungen sein und immer sicherstellen, dass jeder Endpunkt durch einen ordnungsgemäß implementierten Validierungsvertrag gesichert ist, oder wir müssen auf Einschränkungen der Persistenzschicht zurückgreifen. Das gleiche Problem betrifft die Architektur von Microservices. Nur ist es hier noch gravierender, und Einschränkungen auf der Persistenzschicht machen wenig Sinn. Ein weiteres Merkmal, über das man diskutieren könnte, ist die Notwendigkeit, für jede Anfrage, die Daten akzeptiert, Schemata zu definieren. Das bringt Explizitheit, was gut ist, aber es schafft mehr Arbeit, da es normalerweise mehr datenannehmende Endpunkte als Modelle gibt.
The wrap
Die Konventionen von Rails sind nicht skalierbar und eignen sich nicht für komplexe Projekte.
Rails' Modelle sind mit Verantwortlichkeiten überladen, was zu Wartungsproblemen führt.
Modellvalidierungen neigen dazu, mit der Zeit immer mehr Schaden anzurichten, wenn sie mit der Komplexität des Lebens konfrontiert werden. Bedingte Modellvalidierungen treiben allen Entwicklern die Tränen in die Augen.
Modellvalidierungen entsprechen nicht den Standards moderner Systemarchitekturen.
Die wirkliche Verbesserung des Anwendungsdesigns ist die Umstellung des Ansatzes von modellspezifischer Validierung auf anforderungsspezifische Validierung. Dies stellt eine Änderung des Softwaredesigns dar.
{
Anfragespezifische Validierung erhöht die Explizitheit. Die Validierungen werden genau dann ausgeführt, wenn wir sie aufrufen, im Gegensatz zur Modellvalidierung, die bei verschachtelten Ressourcen unkontrolliert abgefeuert wird.
Die anfragespezifische Validierung funktioniert gut mit allen Architekturen, auch mit denen, die Event Messaging verwenden.
{
Mit großer Macht kommt große Verantwortung - Datenintegrität braucht mehr Sorgfalt und Aufmerksamkeit bei anfragespezifischen Validierungen.
{
dry-rb ist ein Schweizer Taschenmesser in der Welt der Ruby-Bibliotheken. Es hilft uns, die Probleme von Rails sauber und effizient zu lösen.
dry-validierungsverträge erleichtern die Datenvalidierung außerhalb der Rails-Modelle und ermöglichen es uns, komplexe Validierungsregeln mit einfacher DSL zu erstellen.
dry-validation contracts bring precision to our code. Ihre DSL ermöglicht es uns, Schemata bis ins kleinste Detail zu erstellen und die Daten an unsere Bedürfnisse anzupassen.
dry-types Typkonzept hilft uns bei der Definition und Wiederverwendung neuer Arten von Basistypen, die in unseren Domänenkontexten auftreten können.
Wir haben kaum an der Oberfläche der dry-rb basierten Validierung gekratzt. Bitte besuchen Sie die dry-rb Hauptseite und überzeugen Sie sich selbst von den Fähigkeiten dieses bösen Buben.
Wir werden in Zukunft vielleicht mehr über fortgeschrittene Fälle und Anwendungen veröffentlichen.
{
Und wir haben definitiv vor, mehr über dry-rb und den Rest ihrer Tools zu posten.
I use word validation a lot. Validation, validation, validate, validating, validation.
That’s all folks.
✍️
ABOUT THE AUTHOR

Mateusz Jarosz
Senior Backend-Entwickler
Senior backend developer