Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Ist die Transaktionsverwaltung im Controller eine schlechte Praxis?

Der Grund, warum ich sage, dass Transaktionen nicht in die Modellschicht gehören, ist im Grunde folgender:

Modelle können Methoden in anderen Modellen aufrufen.

Wenn ein Modell versucht, eine Transaktion zu starten, aber keine Kenntnis darüber hat, ob sein Aufrufer bereits eine Transaktion gestartet hat, muss das Modell bedingt Starten Sie eine Transaktion, wie im Codebeispiel in @Bubbas Antwort gezeigt . Die Methoden des Modells müssen ein Flag akzeptieren, damit der Aufrufer ihm mitteilen kann, ob er eine eigene Transaktion starten darf oder nicht. Oder das Modell muss in der Lage sein, den Zustand „in einer Transaktion“ seines Aufrufers abzufragen.

public function setPrivacy($privacy, $caller){
    if (! $caller->isInTransaction() ) $this->beginTransaction();

    $this->privacy = $privacy;
    // ...action code..

    if (! $caller->isInTransaction() ) $this->commit();
}

Was ist, wenn der Anrufer kein Objekt ist? In PHP kann es sich um eine statische Methode oder einfach um nicht objektorientierten Code handeln. Dies wird sehr chaotisch und führt zu einer Menge wiederholten Codes in Modellen.

Es ist auch ein Beispiel für Control Coupling , was als schlecht angesehen wird, da der Aufrufer etwas über die interne Funktionsweise des aufgerufenen Objekts wissen muss. Zum Beispiel einige der Methoden Ihres Modells kann einen $transactional-Parameter haben, aber andere Methoden haben diesen Parameter möglicherweise nicht. Wie soll der Aufrufer wissen, wann der Parameter wichtig ist?

// I need to override method's attempt to commit
$video->setPrivacy($privacy, false);  

// But I have no idea if this method might attempt to commit
$video->setFormat($format); 

Die andere Lösung, die ich vorgeschlagen (oder sogar in einigen Frameworks wie Propel implementiert) gesehen habe, besteht darin, beginTransaction() zu erstellen und commit() No-Ops, wenn die DBAL weiß, dass sie sich bereits in einer Transaktion befindet. Dies kann jedoch zu Anomalien führen, wenn Ihr Modell versucht, sich zu verpflichten, und feststellt, dass es sich nicht wirklich verpflichtet. Oder versucht, ein Rollback durchzuführen, und diese Anforderung wird ignoriert. Ich habe schon früher über diese Anomalien geschrieben.

Der Kompromiss, den ich vorgeschlagen habe, ist, dass Modelle nichts über Transaktionen wissen . Das Modell weiß nicht, ob es sich um setPrivacy() handelt ist etwas, das sofort festgeschrieben werden sollte, oder ist es Teil eines größeren Bildes, einer komplexeren Reihe von Änderungen, die mehrere Modelle betreffen und nur sollten begangen werden, wenn all diese Änderungen erfolgreich sind. Das ist der Sinn von Transaktionen.

Wenn Models also nicht wissen, ob sie ihre eigene Transaktion beginnen und durchführen können oder sollten, wer weiß es dann? GRASP enthält ein Controller-Muster Dies ist eine Nicht-UI-Klasse für einen Anwendungsfall, und ihr wird die Verantwortung zugewiesen, alle Teile zu erstellen und zu steuern, um diesen Anwendungsfall zu erfüllen. Controller wissen über Transaktionen Bescheid denn dort sind alle Informationen darüber zugänglich, ob der gesamte Anwendungsfall komplex ist und mehrere Änderungen in Modellen innerhalb einer Transaktion (oder vielleicht innerhalb mehrerer Transaktionen) erfordert.

Das Beispiel, über das ich zuvor geschrieben habe, ist das Starten einer Transaktion in beforeAction() -Methode eines MVC-Controllers und übergeben Sie sie in afterAction() Methode, ist eine Vereinfachung . Der Controller sollte die Freiheit haben, so viele Transaktionen zu starten und festzuschreiben, wie es logisch erforderlich ist, um die aktuelle Aktion abzuschließen. Oder der Controller könnte manchmal auf eine explizite Transaktionskontrolle verzichten und den Modellen erlauben, jede Änderung automatisch zu übernehmen.

Aber der Punkt ist, dass die Informationen darüber, welche Transaktion(en) notwendig sind, etwas sind, das die Models nicht wissen – sie müssen ihnen mitgeteilt werden (in Form eines $transactional-Parameters) oder sie sonst von ihrem Aufrufer abfragen, was müsste die Frage ohnehin bis zum Handeln des Verantwortlichen delegieren.

Sie können auch einen Service Layer erstellen von Klassen, die alle wissen, wie man solch komplexe Anwendungsfälle ausführt und ob man alle Änderungen in einer einzigen Transaktion zusammenfasst. Auf diese Weise vermeiden Sie viel wiederholten Code. Aber es ist nicht üblich, dass PHP-Apps einen eigenen Service Layer enthalten; Die Aktion des Controllers fällt normalerweise mit einer Serviceschicht zusammen.