Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Fazer o Gerenciamento de Transações no Controller é uma prática ruim?


A razão pela qual digo que as transações não pertencem à camada de modelo é basicamente esta:

Os modelos podem chamar métodos em outros modelos.

Se um modelo tenta iniciar uma transação, mas não sabe se seu chamador já iniciou uma transação, então o modelo deve condicionalmente iniciar uma transação, conforme mostrado no exemplo de código em @Bubba's answer . Os métodos do modelo têm que aceitar um sinalizador para que o chamador possa dizer se ele tem permissão para iniciar sua própria transação ou não. Ou então o modelo precisa ter a capacidade de consultar o estado "em uma transação" de seu chamador.
public function setPrivacy($privacy, $caller){
    if (! $caller->isInTransaction() ) $this->beginTransaction();

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

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

E se o chamador não for um objeto? Em PHP, pode ser um método estático ou simplesmente um código não orientado a objetos. Isso fica muito confuso e leva a muitos códigos repetidos nos modelos.

Também é um exemplo de Acoplamento de controle , que é considerado ruim porque o chamador precisa saber algo sobre o funcionamento interno do objeto chamado. Por exemplo, algumas dos métodos do seu modelo podem ter um parâmetro $transactional, mas outros métodos podem não ter esse parâmetro. Como o chamador deve saber quando o parâmetro é importante?
// 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); 

A outra solução que vi sugerida (ou mesmo implementada em alguns frameworks como o Propel) é fazer beginTransaction() e commit() no-ops quando o DBAL sabe que já está em uma transação. Mas isso pode levar a anomalias se o seu modelo tentar fazer o commit e descobrir que ele não está realmente confirmado. Ou tenta reverter e essa solicitação é ignorada. Já escrevi sobre essas anomalias antes.

O compromisso que sugeri é que Os modelos não sabem sobre transações . O modelo não sabe se sua solicitação para setPrivacy() é algo que deve ser confirmado imediatamente ou é parte de um quadro maior, uma série mais complexa de alterações que envolvem vários modelos e deve somente ser confirmado se todas essas mudanças forem bem-sucedidas. Esse é o objetivo das transações.

Portanto, se os Modelos não sabem se podem ou devem iniciar e confirmar sua própria transação, quem sabe? GRASP inclui um padrão de controlador que é uma classe não UI para um caso de uso, e a ela é atribuída a responsabilidade de criar e controlar todas as partes para realizar esse caso de uso. Os controladores sabem sobre as transações porque esse é o lugar onde todas as informações são acessíveis sobre se o caso de uso completo é complexo e requer que várias alterações sejam feitas em Modelos, em uma transação (ou talvez em várias transações).

O exemplo sobre o qual escrevi antes, é iniciar uma transação no beforeAction() método de um controlador MVC e confirme-o no afterAction() método, é uma simplificação . O Controlador deve ser livre para iniciar e confirmar quantas transações forem necessárias logicamente para concluir a ação atual. Ou, às vezes, o Controlador pode abster-se do controle de transação explícito e permitir que os Modelos autorizem cada alteração.

Mas o ponto é que a informação sobre quais transacções são necessárias é algo que os Modelos não sabem - eles têm que ser informados (na forma de um parâmetro $transacional) ou então consultar o chamador, o que teria que delegar a questão até a ação do Controlador de qualquer maneira.

Você também pode criar uma Camada de serviço de classes que cada uma sabe como executar casos de uso tão complexos e se deve incluir todas as alterações em uma única transação. Dessa forma, você evita muito código repetido. Mas não é comum que aplicativos PHP incluam uma camada de serviço distinta; a ação do Controlador geralmente coincide com uma Camada de Serviço.