sql >> Databasteknik >  >> RDS >> Mysql

Är det dålig praxis att utföra transaktionshantering i controllern?

Anledningen till att jag säger att transaktioner inte hör hemma i modelllagret är i grunden detta:

Modeller kan anropa metoder i andra modeller.

Om en modell försöker starta en transaktion, men den inte vet om den som ringer redan har startat en transaktion, måste modellen villkorligt starta en transaktion, som visas i kodexemplet i @Bubbas svar . Modellens metoder måste acceptera en flagga så att den som ringer kan tala om för den om det är tillåtet att starta sin egen transaktion eller inte. Annars måste modellen ha möjlighet att fråga uppringarens tillstånd "i en transaktion".

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

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

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

Vad händer om den som ringer inte är ett objekt? I PHP kan det vara en statisk metod eller helt enkelt icke-objektorienterad kod. Detta blir väldigt rörigt och leder till mycket upprepad kod i modeller.

Det är också ett exempel på Kontrollkoppling , vilket anses vara dåligt eftersom den som ringer måste veta något om det anropade objektets interna funktion. Till exempel, några av metoderna i din modell kan ha en $transactional parameter, men andra metoder kanske inte har den parametern. Hur ska den som ringer veta när parametern spelar roll?

// 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); 

Den andra lösningen jag har sett föreslagen (eller till och med implementerad i vissa ramverk som Propel) är att göra beginTransaction() och commit() no-ops när DBAL vet att den redan är i en transaktion. Men detta kan leda till anomalier om din modell försöker begå sig och upptäcker att den inte riktigt begår. Eller försöker återställa och får den begäran ignorerad. Jag har skrivit om dessa anomalier förut.

Kompromissen jag har föreslagit är att modeller inte känner till transaktioner . Modellen vet inte om dess begäran till setPrivacy() är något det bör begå omedelbart eller är det en del av en större bild, en mer komplex serie av förändringar som involverar flera modeller och endast bör vara engagerad om alla dessa förändringar lyckas. Det är poängen med transaktioner.

Så om modellerna inte vet om de kan eller bör börja och genomföra sin egen transaktion, vem gör det då? GRASP innehåller ett Kontrollmönster som är en icke-UI-klass för ett användningsfall, och den tilldelas ansvaret att skapa och kontrollera alla delar för att åstadkomma det användningsfallet. Kontrollansvariga känner till transaktioner eftersom det är där all information är tillgänglig om huruvida hela användningsfallet är komplext och kräver att flera ändringar görs i modeller, inom en transaktion (eller kanske inom flera transaktioner).

Exemplet jag har skrivit om tidigare, det vill säga att starta en transaktion i beforeAction() metod för en MVC-styrenhet och commit den i afterAction() metod, är en förenkling . Kontrollören bör vara fri att starta och utföra så många transaktioner som det logiskt kräver för att slutföra den aktuella åtgärden. Eller ibland kan kontrollanten avstå från explicit transaktionskontroll och tillåta modellerna att autocommitera varje ändring.

Men poängen är att informationen om vilka transaktioner som är nödvändiga är något som modellerna inte vet -- de måste få veta (i form av en $transactional parameter) eller fråga den från deras anropare, vilket skulle behöva delegera frågan hela vägen upp till kontrollantens åtgärd ändå.

Du kan också skapa ett Servicelager av klasser som var och en vet hur man utför sådana komplexa användningsfall, och om alla ändringar ska inkluderas i en enda transaktion. På så sätt slipper du mycket upprepad kod. Men det är inte vanligt att PHP-appar inkluderar ett distinkt Service Layer; kontrollantens åtgärd sammanfaller vanligtvis med ett tjänstelager.



  1. Byt Oracle-port från port 8080

  2. Sväva högre i molnet med MariaDB SkySQL

  3. NLS_LANG inställning för JDBC tunn drivrutin?

  4. Konfigurera en lokal SQL Server-databas