Theme-Editor

MVC-Pattern und (gewollte) Architekturbrüche

Igäl

Mitglied
Registriert
17.10.26
Beiträge
6
Reaktionspunkte
2
Punkte
3
1/3
Themensteller
Hallo Menschen

Dann beginne ich hier mal mit einem aktuellen Thema, das mich umtreibt. Mein altes Framework war eine wilde Durchmischung der verschiedenen Ebenen und nach Jahren einzelnen Nachvollzugs von bestimmten Neuerungen ein wahres Flickwerk und kaum zu bändigen. Darum habe ich direkt alles in den Eimer geschmissen und vor ca. sechs Wochen komplett neu gestartet.

Als ich mich nach langer codeabstinenter Zeit wieder mit dem aktuellen State of the Art beschäftigt habe, bin ich natürlich um das MVC-Pattern nicht herumgekommen. Wenn auch schon länger Standard, hat es in meinem Code, der vor 20 Jahre seinen Anfang gefunden hatte, noch keinen Einzug gefunden. Und so habe ich mich da mal profunder eingelesen und bin direkt auf einige Tradeoffs gestossen, dich ich nun sukzessive aufzulösen versuche.

Einer davon betrifft den Grundsatz, dass prinzipiell schlanken Controllern den Vorzug zu geben ist. Das leuchtet von daher ein, dass in einem Framework immer mal wieder Logik auftaucht, die wiederverwendbar sein sollte und entsprechend universell und nicht in einem proprietären Controller vorhanden sein sollte. So wie ich das verstanden habe, hat der Controller nur die Aufgabe Input entgegen zu nehmen, entsprechend dem Input in der Model-Schicht Geschäftslogik anzukicken, von der Geschäftslogik Daten zu empfangen und an den View weiterzureichen.

Entsprechend habe ich ziemlich viel, was vorher repetitiver Spaghetticode war, gekapselt. Namentlich duzende hard-coded Formulare und Listen mit List- und FormHandler-Klassen ersetzt. Hier stellt sich mir aber bereits die erste Architekturentscheidung, die das Potential hat, die Regeln des MVC-Pattern zu brechen oder zumindest zu ritzen.

Nach meinem Verständnis gehört der Aufbau eines Formulars (FormFactory liefert FormHandler, der mit Objekten des Typs FormElement gefüllt wird) nicht in den Controller, obwohl das natürlich eng mit der View-Schicht verwandt ist. Wenn ich ein rigides Verständnis des Patterns anwenden würde, müsste ich für jedes Formular eine Hilfsklasse erstellen.

Also kann ich hier eigentlich wählen: entweder ich befolge stur, was das Pattern als immanenten Grundsatz verankert hat und habe einen veritablen Hilfsklassenwildwuchs. Oder ich verzichte auf die sture Befolgung des Grundsatzes und habe dafür Controller die nicht mehr 'skinny' sind.

Ich habe mich hier vorerst für einen Kompromiss entschieden: Formulare, die unique sind, also nirgends ein zweites Mal gebraucht werden (bspw. Insert-News-Formular, das nur im Adminbereich in der Sektion News gebraucht wird) generiere ich im Controller über private Hilfsmethoden. Formulare die an mindestens zwei Orten gebraucht werden (bspw. das Formular um News zu filtern, das einerseits in der News-List im Adminbereich aber auch im News-Anzeigebereich des Userbereichs genutzt wird) kapsle ich in einer eigenen Hilfsklasse und binde sie dort ein, wo sie gebraucht werden.

Die Fragen hier mal ganz allgemein:
- Wendet ihr die Pattern-Rules konsequent an?
- Falls nein: nach welchen Kriterien entscheidet ihr euch, von den best practices abzuweichen?
- Falls mein Kompromiss nicht allzu jenseits ist - was ist stärker zu gewichten: skinny controller oder Verhindern einer Hilfsklassenschwemme?

Ganz allgemein sehe ich, wie mächtig die aktuell herrschenden Grundsätze sind und den sukzessiven Aufbau von wirklich grossen Tools wunderbar unterstützen. Deshalb werde ich mich hier noch weiter reinfuchsen und bin am Austausch interessiert.

Weiterhin ein gutes Wochenende allerseits.

Gruss
 
Hi,

Ich würde das Dilemma gar nicht so stellen. »Skinny Controller« meint keine Geschäftslogik im Controller, nicht möglichst kurze Controller. Eine Formulardefinition ist keine Geschäftslogik, aber auch kein Controller-Job, sondern Struktur für Ein- und Ausgabe. Kriterium für die Platzierung wäre für mich die Verantwortlichkeit, nicht die Anzahl der Verwendungsorte. Sonst musst du jedes Formular umziehen, sobald es ein zweites Mal gebraucht wird.

Und viele kleine Klassen sind kein Wildwuchs: Symfony macht pro Formular eine FormType-Klasse, Laravel pro Request eine FormRequest-Klasse. Wenn dich die Masse stört, könntest du die Formulare auch deklarativ als Config/Array beschreiben und von deiner FormFactory bauen lassen. Dann brauchst du weder Hilfsmethoden im Controller noch eine Klasse pro Formular.

Die Validierung würde ich übrigens noch mal getrennt betrachten: Regeln, die fachlich sind (»Titel muss eindeutig sein«), gehören ins Model, nur formale (»Pflichtfeld«, »E-Mail-Format«) ins Formular.

Eine Frage noch: Wo landet bei dir das HTML?
Wenn die FormElement-Klassen sich selbst rendern, würde ich Definition und Rendering trennen, also die Klassen nur beschreiben lassen (Typ, Label, Regeln) und das Markup ins Template oder einen separaten Renderer auslagern. Dann bleibt auch die Controller-Frage entspannter, weil der Controller nur noch das Formularobjekt weiterreicht.

Grüße
 
Themensteller
Hey JFS

So wie ich dich verstanden habe, bist du da in der Umsetzung des Patterns relativ strikt und kannst dir vorstellen, für jedes Formular eine eigene Klasse zu bauen. Vielleicht habe ich mich auch etwas unpräzise ausgedrückt.

Prinzipiell bin ich - glaube ich - schon relativ deklarativ unterwegs. Ein Formular wird nach dem folgenden Muster erstellt und weitergereicht (freihändiger PHP-Pseudocode):

PHP:
$actualForm = $this->formFactory->create()
    ->addElement(new FormElement(type: 'hidden', name: 'f-foo-id', value: 0))
    ->addElement(new FormElement(...))
    ->addRule(name: 'f-foo-name', rule: 'required', error: 'Da muss was rein!')
    ->addElement(new FormElement(type: 'submit', ...));

Das passiert bei mir in den Controllern (sofern das Formular halt eben nur in dem einen Controller gebraucht wird) und kann, je nach Formular, ziemliche Ausmasse annehmen. Und eben wegen dieser Ausmasse, habe ich das in Hilfsmethoden (bei einmaliger Nutzung) oder Hilfsklassen (bei mehrmaliger Nutzung) ausgelagert. Um die "Hauptmethode", die vom Router aufgerufen wird, schlank und lesbar zu halten.

Ich war der Meinung, dass diese Deklaration in den Controller gehört, weil nur der Controller weiss, was danach an den View (bei mir via Twig-Rendering vom jeweiligen Controller und nicht von der FormFactory aus) geliefert werden muss. In meinem Verständnis weiss nur der Controller, welche Formulare im Frontend gebraucht werden. Damit hat die Model-Schicht meines Erachtens nichts zu tun. Aber eben... diese Deklaration bläht halt den Controller relativ stark auf und macht ihn - vom Umfang her - "fat".

So wie ich dich nun verstanden habe, ist der Umfang aber eigentlich zweitrangig. Wichtig ist, dass der Controller bezüglich die Zuständigkeiten skinny bleibt. Entsprechend ist ein Controller dann skinny, wenn er nur Sachen verarbeitet, die in seiner Zuständigkeit liegen. Habe ich das so korrekt interpretiert?

Und ja: addRule appliziert nur Formalien wie 'required', 'readonly', 'placeholder' etc. Die eigentliche Datenüberprüfung findet im Service statt.
 
Zurück
Oben