Thread Catalyst - DBIx::Class - Model - Diskussion
(3 answers)
Opened by sid burn at 2009-02-01 17:39
Hi,
ich setze mich derzeit verstärkt mit Catalyst und natürlich auch DBIx::Class auseinander. Wenn ich aber an das MVC Kozept denke, dann denke ich mir das DBIx::Class und so wie es oft verwendet wird irgendwie nicht ganz in das Konzept passt, diesen Punkt werde ich jetzt etwas genauer erklären. Ansonsten geht es in meinen Beispiel jetzt ums Prinzip, also nicht sagen das Hsitorisch gesehen dieses oder jenes Modul nicht gab etc. Danke. Nehmen wir an wir haben ein Programmierer der bereits vor 20 Jahren eine Webanwendung nach dem MVC Konzept geschrieben hat. Da früher Datenbanken noch nicht so weit verbreitet waren und es noch wenige Alternativen gab, entschied er sich als Datenspeicher eine simple CSV Datei zu nehmen. Da zu dem Zeitpunkt auch nicht mit so einem hohen Datensatz aufkommen zu rechnen war, reichte das ganze auch noch aus. In diesem Beispiel speichert er einfach ein paar Informationen zu einem Benutzer. Sein Model sah dann ungefähr so aus. Code: (dl
)
1 package Model::CSV; Code: (dl
)
1 package Schema::User; Seine Webapplikation läuft nun ein paar Jahre, in seinen Controllern finden sich ein haufen aufrufe von "find_user($user) etc. Da die CSV Datei immer größer geworden ist, das Auslesen und setzen immer länger dauert und da mit den Jahren immer mehr freie Datenbanken erschienen sind, entscheidet er sich DBI mit einer Datenbank zu nutzen. Durch die aufteilung des MVC ist er jetzt in der Lage diese Umstellung zu machen find_user() könnte z.B. so aussehen. Code: (dl
)
1 sub find_user { Was er letztendlich getan hat ist eine komplette Umstellung der CSV Basierten Speicherung auf eine Datenbank. Was musste er im Controller anpassen? Absolut gar nichts. Den die Controller haben das Abstrakte Model genutzt das den Zugriff intern regeln. Er hat also den Daten zugriff geändert und nur das Model verändert. So wie es auch sein sollte. Den das Model soll ja auch eine Abstraktion für den Datenzugriff darstellen. Es ist sogar möglich das er später einmal auf Class::DBI umstellt und später dann auf DBIx::Class. Er passt dafür immer nur das Model an, und das war es. In DBIx::Class könnte der Zugriff dann so ausschauen. Code: (dl
)
1 sub find_user { Jetzt zurück zur Gegenwart. In Catalyst nutzen wir im Model ein Schema das den Datenzugriff darstellt. Innerhalb von Catalyst könnten wir z.B. mit Code: (dl
)
$c->model('DB::User')->find({ user => $user }); unseren Benutzer bekommen. In der Regel wird das auch so gemacht, mitten im Controller. Und hier ist bereits das Problem. Wir haben keine Datenabstraktion mehr was uns das Model eigentlich bieten sollte. Mit "$c->model('DB::User')" bekommen wir einen ResultSet zurück und wir benutzen das Interface direkt! Das wäre vergleichbar gewesen als wenn unser Programmierer der von 20 Jahren sein Model geschrieben hat nicht eine neue Klasse für den Zugriff bereit stellt sondern uns im Controller einfach nur das CSV Modul bereit gestellt hätte und wir hätten das CSV Modul direkt genutzt um auf die Daten zuzugreifen und um unsere Einträge zu ändern. Was wäre die Folge gewesen? Er hätte seine änderungen nicht mehr so machen können wie ich es beschrieben habe. Den seine Controller nutzen direkt das "dadrunterliegende" Interface um die Daten auszulesen/ändern/erstellen. Ein wechsel zu DBI hätte bedeutet er hätte alle Controller anpassen müssen die sein Datenmodel nutzen und anstatt dort CSV Zugriffe zu nutzen hätte er direkt im Controller DBI genutzt und dort SQL hineingeschrieben. Kurz gesagt, das was das "Model" eigentlich liefern sollte wurde komplett verfehlt. Es ist absolut gar keine Abstraktion vorhanden. Änderungen im Model bedeuten gleichzeitig auch das änderungen im Controller geschehen müssen. Selbst das simple DBIx::Class Beispiel verstößt dort schon dagegen. Code: (dl
)
$c->model('DB::User')->find({ user => $user }); Den in diesem Beispiel sagen wir ja schon das wir einen benutzer haben wollen dessen "user" Spalte in der Datenbank $user ist. Sollte sich unser Model ändern, z.B. entscheiden wir uns "username" im Model zu verwenden dann sind alle Zugriffe im Controller kaputt. Eigentlich sollte uns die Abstraktion als Model uns vor soetwas ja bewahren. Hätte man ein eigenes ResultSet genommen und eine eigene Methode "find_user" eingefügt und hätte diese dann genommen anstatt die "find" Methode zu nutzen die das ResultSet anbietet hätten wir diese Änderungen machen können. Im Controller würde dann soetwas stehen wir folgendes. Code: (dl
)
$c->model('DB::User')->find_user($user); und im ResultSet dann folgende Methode. Zwar ist das wenig Code das eine Abstraktion schon fast nicht wert ist (so denkt man), aber wenn wir unsere Datenbank wirklich anpassen sollten und aus dem Feld "user" ein "username" machen würden, müssten wir nur das Model anpassen. Die Controller die unser Model nutzen müssen nicht angepasst werden. Ansonsten ist das ja noch ein simples beispiel. komplexe find, search abfragen wo mit prefetch etc. gearbeitet wird schreiben wohl auch die meisten direkt im Controller. Die komplette Arbeitsweise so wird einem ja auch sogar gleich so im Catalyst Tutorial beigebracht. Und jetzt halt die endgültige Frage. Wie steht ihr dazu? Wie seht ihr das ganze? Letztendlich ist eine echte Abstraktion des Model so nicht erreicht. Sollte sich am Model oder der Datenbank etwas ändern müssen evtl. die Zeilen im Controller angepasst werden. Schreibt man solche Zeilen im Controller sind sie nicht wiederbenutzbar. Das Model ist nicht mehr veränderbar. Hätte man vor paar Jahren noch Class::DBI genutzt und das Interface genau so benutzt wäre ein Umstieg zu DBIx::Class wohl nicht mehr möglich gewesen (oder erschwert gewesen). Auser es wird das komplett gleiche Interface angeboten (was in diesem fall glaube ich sogar der fall war). Letztendlich ist solch eine Benutzung derzeit nur eins. Eine vereinfachung gegenüber der verwendung von z.B. DBI. Einen großen Unterschied macht es nicht ob wir im Controller direkt DBIx::Class nutzen oder ob wir dort direkt das DBI Interface nutzen und unser SQL dort direkt hereinschreiben. Wir haben dadurch keine Abstraktion gewonnen, sollte ein anderes Modul auf unsere daten zugreifen muss es wieder das gleiche SQL nutzen nur steht es dann an zwei stellen, anstatt etwas bereits vorhandenes einfach wieder benutzt wird, weil man es in eine Subroutine ausgelagert hatte. Also wie steht ihr zu dem ganzen? Habt ihr daran bisher noch nicht gedacht? Ist es egal da ihr meint das ihr sowieso nie das Model ändert? Zu komplex/großer aufwand das ganze nochmals zu abstrahieren? Oder habt ihr eine gute Idee wie man das ganze besser Abstrahieren kann? Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
|