Transação é na Camada de Negócio ou Persistencia?

Veja temos um sistema em 3 camadas (Tela, Negócio e Persistencia)

Existem regras de negócio que exige que todo o procedimento esteja em transação, ao meu ver, transação é algo que deve ficar na camada de persistência, e regra de negócio é algo que deve ficar na camada de negócio. Como faço então?

Abro transação e faço commit na camada de negócio?
Ou jogo a regra de negócio na camada de persistência por causa da transação?

Abs
Rodrigo

[quote=rsantik]Veja temos um sistema em 3 camadas (Tela, Negócio e Persistencia)

Existem regras de negócio que exige que todo o procedimento esteja em transação, ao meu ver, transação é algo que deve ficar na camada de persistência, e regra de negócio é algo que deve ficar na camada de negócio. Como faço então?

Abro transação e faço commit na camada de negócio?
Ou jogo a regra de negócio na camada de persistência por causa da transação?

Abs
Rodrigo[/quote]

Cara,
Não sei se você está usando MVC, mas geralmente Persistência faz parte da camada de Lógica de Negócios e não como uma camada independente, justamente por ser uma transação.

Imagina Assim:
Viewer
- Telas do Sistema
Modeler(Lógica de Negócio)
-Persistência
-POJO(Setters e Getters)
Controler
-faz a interface entre View e Model

Abraços.
Att.

Amigo, pelo que entendi, essa transação que você fala é sobre o acesso a banco de dados, então, ela tem q ficar na camada de persistencia. Esse lance de Commit, abrir transação, etc.

[quote=r-ngarcia][quote=rsantik]Veja temos um sistema em 3 camadas (Tela, Negócio e Persistencia)

Existem regras de negócio que exige que todo o procedimento esteja em transação, ao meu ver, transação é algo que deve ficar na camada de persistência, e regra de negócio é algo que deve ficar na camada de negócio. Como faço então?

Abro transação e faço commit na camada de negócio?
Ou jogo a regra de negócio na camada de persistência por causa da transação?

Abs
Rodrigo[/quote]

Cara,
Não sei se você está usando MVC, mas geralmente Persistência faz parte da camada de Lógica de Negócios e não como uma camada independente, justamente por ser uma transação.

Imagina Assim:
Viewer
- Telas do Sistema
Modeler(Lógica de Negócio)
-Persistência
-POJO(Setters e Getters)
Controler
-faz a interface entre View e Model

Abraços.
Att.[/quote]

Certo, vamos por partes. MVC nao tem a ver com a divisão em camadas lógicas. MVC é um padrão para separar a camada de apresentação do resto da sua aplicação (isolar a GUI), para isso faz uso de um controller. o M do MVC é todo o resto, incluindo camada de regras de negocio, persistencia, procedures, arquivos e etc…

Nenhum dos exemplos que o r-ngarcia citou está em desacordo com isso, mas só a menção do MVC num tópico sobre camadas lógicas já pode causar confusão.

Quanto a dúvida: Como eu prefiro fazer, e faço sempre que posso, eu processo tudo que preciso relacionado a negócios antes de abrir uma transação. Supondo que estamos falando de um sistema bancário, eu, antes de começar a processar, trago a conta da qual será debitado o valor, a conta na qual será depositado o valor e o valor propriamente dito. Depois de ter trazido faço as operações em memória, sem nenhuma transação aberta, nem de consulta, nem de gravação. Após ter colocado os objetos no estado em que devem ficar, no exemplo, depois de ter movido o dinheiro da conta A para a B, eu abro a transação, guardo o estado atual dos objetos e commito (ou não) a transação.

Resumindo, grosso modo, eu busco tudo que preciso usando a camada de persistência, na camada de negócios aplico as regras que devem ser aplicadas e finalmente envio os objetos novamente para a camada de persistência para que tenham seu novo estado gravado.

Você pode ter transação de negócio e de persistência. A de negócio poderia ser gerenciada pelo Spring por exemplo. Vou tentar criar um código de exemplo aqui:

[code]@Service
public class PedidoCompraService {

@Autowired
private PedidoCompraRepository repository;

@Autowired
private EstoqueService estoqueService;

@Autowired
private ContasAReceberService contasAReceberService;

@Transactional
public void incluir(PedidoCompra pedido) {
    estoqueService.reservar(pedido.getListaProdutos());
    contasAReceberService(pedido.getDetalhes());
    repository.adicionar(pedido);
}

}[/code]
Bem, isso tudo é transação de negócio. Pra salvar isso de fato no banco de dados você também vai ter transações de persistência.

O que acontece é que sua transação de persistência vai depender também da transação de negócio.

Se ao registrar contas a receber, é verificado que o cliente está negativado e lança uma exceção, todas as operações deverão ser canceladas. Isso tudo é negócio.

Se ao salvar no banco o pedido final e ocorrer um erro (sintaxe, BD fora do ar, etc). Tudo também deve ser cancelado, devido a um erro na persistência.

Transação de Negócio é maior e contém a Transação de Persistência.

1 curtida

[quote=YvGa][quote=r-ngarcia][quote=rsantik]Veja temos um sistema em 3 camadas (Tela, Negócio e Persistencia)

Existem regras de negócio que exige que todo o procedimento esteja em transação, ao meu ver, transação é algo que deve ficar na camada de persistência, e regra de negócio é algo que deve ficar na camada de negócio. Como faço então?

Abro transação e faço commit na camada de negócio?
Ou jogo a regra de negócio na camada de persistência por causa da transação?

Abs
Rodrigo[/quote]

Cara,
Não sei se você está usando MVC, mas geralmente Persistência faz parte da camada de Lógica de Negócios e não como uma camada independente, justamente por ser uma transação.

Imagina Assim:
Viewer
- Telas do Sistema
Modeler(Lógica de Negócio)
-Persistência
-POJO(Setters e Getters)
Controler
-faz a interface entre View e Model

Abraços.
Att.[/quote]

Certo, vamos por partes. MVC nao tem a ver com a divisão em camadas lógicas. MVC é um padrão para separar a camada de apresentação do resto da sua aplicação (isolar a GUI), para isso faz uso de um controller. o M do MVC é todo o resto, incluindo camada de regras de negocio, persistencia, procedures, arquivos e etc…

Nenhum dos exemplos que o r-ngarcia citou está em desacordo com isso, mas só a menção do MVC num tópico sobre camadas lógicas já pode causar confusão.

Quanto a dúvida: Como eu prefiro fazer, e faço sempre que posso, eu processo tudo que preciso relacionado a negócios antes de abrir uma transação. Supondo que estamos falando de um sistema bancário, eu, antes de começar a processar, trago a conta da qual será debitado o valor, a conta na qual será depositado o valor e o valor propriamente dito. Depois de ter trazido faço as operações em memória, sem nenhuma transação aberta, nem de consulta, nem de gravação. Após ter colocado os objetos no estado em que devem ficar, no exemplo, depois de ter movido o dinheiro da conta A para a B, eu abro a transação, guardo o estado atual dos objetos e commito (ou não) a transação.

Resumindo, grosso modo, eu busco tudo que preciso usando a camada de persistência, na camada de negócios aplico as regras que devem ser aplicadas e finalmente envio os objetos novamente para a camada de persistência para que tenham seu novo estado gravado.
[/quote]

Realmente, eu faço isso mesmo, antes de abrir transação no banco, procuro utilizar a camada de negócio. É que sempre há regras de negócio que na sua totalidade, devem ser feitos sob transação, e até agora não consegui achar um jeito certo para tratar esses casos. O que estamos fazendo é jogando regras de negócio na camada de persistencia, apenas por causa da Transação com o banco. Fogo, né!! rs

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 :slight_smile:

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=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 :slight_smile:

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).

Perfeito, então vou conversar com o pessoal aqui, ve o que eles acham, para abrir a Transação com banco na camada de Negócio e fazer commit ou roolback na negócio, chamando os métodos da persistencia necessários segundo a regra de negócio a ser aplicada.

Obrigado a todos pela ajuda!!

Abs
Rodrigo