[quote=gomesrod]A implementação da transação propriamente é parte da camada de persistência… estou falando das chamadas “baixo nível”, dependentes do mecanismo utilizado: conectar, beginTransaction, commit, rollback…
Já o momento em que essas chamadas são feitas estão fora do alcance da camada de persistência, dependem do negócio. Você já deve ter percebido isso, mas vou colocar um exemplo - a boa e velha transferência bancária:
// DISCLAIMER: Substitua persistenciaContaCorrente pelo seu padrão de persistência preferido - DAO, Repository, Active Records...
efetuarTransferencia() {
// ...
// Aqui estou na camada de negócio. Já passamos por todas as validações necessárias, atribuímos
// o novo saldo para ambas as contas e chegamos ao ponto em que o comportamento transacional é inevitável.
persistenciaContaCorrente.salvar(contaSacada);
persistenciaContaCorrente.salvar(contaDestino);
// ...
}
O método salvar() não pode ser responsável por abrir e fechar as transações, porque via de regra ele não sabe o que mais vai ser feito dentro dessa operação de negócio. Ao mesmo tempo, o método efetuarTransferencia() não pode mexer com isso diretamente, porque é responsabilidade da persistência conhecer os detalhes da coisa.
Esse é o problema. Até agora nenhuma novidade, só coloquei a sua própria pergunta com outras palavras
Finalmente vou começar a responder:
O que deve ser feito é: permitir que a camada de negócio controle as transações, mas de uma maneira abstrata. Quer dizer, ela sabe quando iniciar, commitar, rollbackar (?), mas sem exatamente saber como funciona por dentro.
A maneira mais fácil de conseguir isso é usar um framework para gerenciar as transações. Por exemplo, com EJB/JPA a execução do seu método de negócio automaticamente é uma transação única, e no final é feito commit (se completou normalmente) ou rollback (se houver exceção). Com Spring é a mesma coisa.
Então se tiver oportunidade, adote uma dessas soluções.
Agora se não puder, uma abordagem possível é criar sua própria abstração sobre as transações, isolando os detalhes.
Exemplo:
efetuarTransferencia() {
Transacao trans = new Transacao();
//..
persistenciaContaCorrente = new PersistenciaContaCorrente(trans);
// ...
// Aqui estou na camada de negócio. Já passamos por todas as validações necessárias, atribuímos
// o novo saldo para ambas as contas e chegamos ao ponto em que o comportamento transacional é inevitável.
try {
persistenciaContaCorrente.salvar(contaSacada);
persistenciaContaCorrente.salvar(contaDestino);
} catch (Exception e) {
trans.rollback();
// Prossegue com o tratamento da exceção.
}
trans.commit();
}
Essa classe Transacao contém o que é preciso para manter a transação entre chamadas à camada de persistência. Por exemplo, pode encapsular uma conexão JDBC ou uma Session do hibernate.
Ela pode ser fornecida para diferentes objetos de persistência, permitindo transações em que participam múltiplas entidades.[/quote]
Perfeito…
Uma técnica que costumo utilizar é criar um objeto GerenciadorTransacao (TransactionManager), dependente de um GerenciadorConexao (ConnectionManager), sendo que o GerenciadorConexao é uma dependência dos objetos de persistência e injetado pela camada de negócios…
Dentro dele há a implementação de que a conexão e transação deve permanecer aberta em uma sessão enquanto não for finalizada com commit ou rollback… por exemplo em uma implementação JDBC…
Desta forma a implementação fica separada da lógica de negócios e até mesmo da lógica de persistência (que não deve gerenciar transação para poder ser reaproveitada).