De reden dat ik zeg dat transacties niet in de modellaag thuishoren, is eigenlijk deze:
Modellen kunnen methoden in andere modellen aanroepen.
Als een model een transactie probeert te starten, maar niet weet of de beller al een transactie heeft gestart, moet het model voorwaardelijk start een transactie, zoals weergegeven in het codevoorbeeld in @Bubba's antwoord . De methoden van het model moeten een vlag accepteren, zodat de beller kan vertellen of het zijn eigen transactie mag starten of niet. Of anders moet het model de mogelijkheid hebben om de "in een transactie"-status van de beller op te vragen.
public function setPrivacy($privacy, $caller){
if (! $caller->isInTransaction() ) $this->beginTransaction();
$this->privacy = $privacy;
// ...action code..
if (! $caller->isInTransaction() ) $this->commit();
}
Wat als de beller geen object is? In PHP kan het een statische methode zijn of gewoon niet-objectgeoriënteerde code. Dit wordt erg rommelig en leidt tot veel herhaalde code in modellen.
Het is ook een voorbeeld van Control Coupling , wat als slecht wordt beschouwd omdat de beller iets moet weten over de interne werking van het opgeroepen object. Bijvoorbeeld, sommige van de methoden van uw model hebben mogelijk een parameter $transactional, maar andere methoden hebben die parameter mogelijk niet. Hoe moet de beller weten wanneer de parameter ertoe doet?
// 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);
De andere oplossing die ik heb voorgesteld (of zelfs geïmplementeerd in sommige frameworks zoals Propel) is om beginTransaction()
te maken en commit()
no-ops wanneer de DBAL weet dat het al in een transactie zit. Maar dit kan tot anomalieën leiden als uw model probeert te committen en ontdekt dat het niet echt commit. Of probeert terug te draaien en dat verzoek wordt genegeerd. Ik heb eerder over deze anomalieën geschreven.
Het compromis dat ik heb voorgesteld, is dat Modellen niets weten over transacties . Het model weet niet of zijn verzoek aan setPrivacy()
is iets dat onmiddellijk moet worden vastgelegd of maakt het deel uit van een groter geheel, een complexere reeks wijzigingen waarbij meerdere modellen betrokken zijn en die alleen worden gepleegd als al deze veranderingen slagen. Dat is het punt van transacties.
Dus als Modellen niet weten of ze kunnen of moeten beginnen en hun eigen transactie moeten plegen, wie dan wel? GRASP bevat een Controllerpatroon wat een niet-UI-klasse is voor een use-case, en het is de verantwoordelijkheid toegewezen om alle onderdelen te maken en te beheren om die use-case te bereiken. Controllers weten van transacties omdat dat de plaats is waar alle informatie toegankelijk is over de vraag of de volledige use case complex is en dat er meerdere wijzigingen moeten worden aangebracht in modellen, binnen één transactie (of misschien binnen meerdere transacties).
Het voorbeeld waar ik eerder over heb geschreven, dat is om een transactie te starten in de beforeAction()
methode van een MVC-controller en zet deze vast in de afterAction()
methode, is een vereenvoudiging . De verwerkingsverantwoordelijke moet vrij zijn om zoveel transacties te starten en door te voeren als logischerwijs nodig is om de huidige actie te voltooien. Of soms kan de controller afzien van expliciete transactiecontrole en de modellen toestaan om elke wijziging automatisch door te voeren.
Maar het punt is dat de informatie over welke transactie(s) nodig zijn, iets is dat de modellen niet weten -- ze moeten verteld worden (in de vorm van een $transactional parameter) of anders opvragen bij hun beller, die zou de vraag hoe dan ook moeten delegeren tot aan de actie van de Verwerkingsverantwoordelijke.
U kunt ook een Servicelaag maken van klassen die elk weten hoe dergelijke complexe use-cases moeten worden uitgevoerd, en of alle wijzigingen in één transactie moeten worden opgenomen. Zo voorkom je veel herhaalde code. Maar het is niet gebruikelijk dat PHP-apps een aparte servicelaag bevatten; De actie van de Verwerkingsverantwoordelijke valt meestal samen met een Servicelaag.