Transação em Repository

Estudando e aplicando mais o repository nas entidades, cheguei à um impasse: como controlar minhas transações nos repositórios (sem usar Spring)?

Exemplo:

public class Conta {

	private ContaRepository repository;

	public void transfere(Conta destino, BigDecimal valor) {
		//logica
		repository.saca(this, valor);
		repository.deposita(destino, valor);
	}
}

Como controlaria a transação nesse caso?

Acho que você está confundindo o propósito do pattern Repository em DDD. Um Repository simula uma coleção de objetos em memória. Ele sabe retornar objetos baseado em algum critério de busca, persistir um objeto recém criado e atualizar objetos existentes. Métodos como sacar(), depositar() não pertencem a um Repositório e sim à própria Entidade. Para um exemplo de uma interface de Repository: http://dddsample.sourceforge.net/xref/se/citerus/dddsample/domain/model/cargo/CargoRepository.html

A transação como sempre deve ser controlada na camada de Serviço e não no Repository.

[quote=esmiralha]Acho que você está confundindo o propósito do pattern Repository em DDD. Um Repository simula uma coleção de objetos em memória. Ele sabe retornar objetos baseado em algum critério de busca, persistir um objeto recém criado e atualizar objetos existentes. Métodos como sacar(), depositar() não pertencem a um Repositório e sim à própria Entidade. Para um exemplo de uma interface de Repository: http://dddsample.sourceforge.net/xref/se/citerus/dddsample/domain/model/cargo/CargoRepository.html

A transação como sempre deve ser controlada na camada de Serviço e não no Repository.[/quote]

Acho que vc não entendeu a aplicação do pattern nesse caso:

Meu modelo NUNCA vai conhecer o DAO, o DAO vai implementar meu Repository, mas como não conheço o dao, e as ações de persistencia ficarão a cargo do repository, preciso ter as transações em algum lugar, aí que entra minha pergunta.

Eu, quando nao uso spring, ponho as transacoes nos services, que sao quem orquestra toda a operacao. No seu caso eu nao usaria a conta para fazer a transferencia e sim uma outra entidade ou um service mesmo, o qual seria o responsavel pelo saque numa conta e deposito na outra.

Pessoalmente acho que uma conta nao deveria ter a responsabilidade de fazer o deposito/transferencia em outra.

[quote=YvGa]Eu, quando nao uso spring, ponho as transacoes nos services, que sao quem orquestra toda a operacao. No seu caso eu nao usaria a conta para fazer a transferencia e sim uma outra entidade ou um service mesmo, o qual seria o responsavel pelo saque numa conta e deposito na outra.

Pessoalmente acho que uma conta nao deveria ter a responsabilidade de fazer o deposito/transferencia em outra.[/quote]

Mas na minha arquitetura, não tenho service, é só assim:

Controller - Entidades - Repositorios

Acho que seu código deveria ser assim, não?

[code]public class Conta {

private ContaRepository repository;

public void transfere(Conta destino, BigDecimal valor) {
	this.saca(valor);
	destino.deposita(valor);
	repository.salvar(this);
	repository.salvar(destino);
}

}
[/code]

Se não me engano é nesses casos que você usa uma UnitOfWork. Cria ela no inicio da HttpRequest, injeta nos seus repositorios e comita no final requisição.

[quote=Oenning]Acho que seu código deveria ser assim, não?

[code]public class Conta {

private ContaRepository repository;

public void transfere(Conta destino, BigDecimal valor) {
	this.saca(valor);
	destino.deposita(valor);
	repository.salvar(this);
	repository.salvar(destino);
}

}
[/code]

Se não me engano é nesses casos que você usa uma UnitOfWork. Cria ela no inicio da HttpRequest, injeta nos seus repositorios e comita no final requisição.[/quote]

Bem isso, mas no caso de um repositório ser uma interface, eu injetaria como? :roll:

Você injeta na implementação, pelo visto no seu caso é uma DAO :slight_smile:

[quote=Oenning]Você injeta na implementação, pelo visto no seu caso é uma DAO :slight_smile:
[/quote]

Mas meu entityManager, por exemplo, seria injetado no DAO através de DI. Aí não vejo uma solução de capturar ele.

Agora você me pegou, eu programo em C#, mas acredito que em Java seja parecido.
Para mim, a injeção vem através do Construtor. Fica assim:

[code]public class MeuRepositorioSqlServer : MeuRepositorio {
private Session session;

public MeuRepositorioSqlServer(Session session) {
    this.session = session;
}

}[/code]

No meu framework DI eu configuro como ele deve injetar uma session.

Quando eu trabalhei com spring+java, bastava anotar a propriedade com algo assim no meu DAO:

@PersistenceContext(algumacoisa="outracoisa") private EntityManager entityManager;

Não sei como estão as coisas atualmente.

É isso que quero dizer, até este ponto, ok, mas como é o container de DI que vai injetar, eu não vou ter a transaction, entende?

Veja se isso ajuda
http://community.jboss.org/wiki/OpenSessioninView#Using_an_interceptor

Ele abre uma transaction no inico da request e comita no final dela.
Sendo assim, durante a requisição, quando seus DAO fizeram INSERT/UPDATE, você vai ter uma transação rodando.

Desculpe-me por não ter mais detalhes, faz tempo que não programo em Java.

[quote=Oenning]Veja se isso ajuda
http://community.jboss.org/wiki/OpenSessioninView#Using_an_interceptor

Ele abre uma transaction no inico da request e comita no final dela.
Sendo assim, durante a requisição, quando seus DAO fizeram INSERT/UPDATE, você vai ter uma transação rodando.

Desculpe-me por não ter mais detalhes, faz tempo que não programo em Java.[/quote]

Legal essa abordagem, vou dar uma estudada.

evite transações e implemente transações compensatórias.

vera que o design pode ficar mais simples.

nesse caso: vc quer transferir de A para B.

vc “saca” de A
vc transfere para B
deu problema? vc extorna para A, depois de X minutos – como acontece na prática com um DOC ou TED.

não consegue extornar por problema no banco? vc tem um belo problema.

se o Saca e Transfere forem eventos, a coisa fica bem interessante.

ai perceba o seguinte: imagine que vc sacou 2x 200 reais ao mesmo tempo e vc tem 201 reais na conta. simultaneo mesmo. provavelmente vc vai atender aos dois requests e, quando ficar negativo e o cara náo puder ficar negativo mete uma multa nele.

mas ai vc não quer que isso aconteça. vc vai ter que implementar algum recurso de “lock” para evitar problemas de simultaneidade. Por exemplo, vc pode gerar um Token de autorização por conexao sendo que o Token que vale é sempre um, que seria salvo em uma estrutura de dados xpto. na hora de responder ao saque vc pode ver se o Token é valido e como só pode haver um… no fim tudo é uma questão de “quem quer rir tem que fazer rir”.

[quote=peczenyj]evite transações e implemente transações compensatórias.

vera que o design pode ficar mais simples.

nesse caso: vc quer transferir de A para B.

vc “saca” de A
vc transfere para B
deu problema? vc extorna para A, depois de X minutos – como acontece na prática com um DOC ou TED.

não consegue extornar por problema no banco? vc tem um belo problema.

se o Saca e Transfere forem eventos, a coisa fica bem interessante.

ai perceba o seguinte: imagine que vc sacou 2x 200 reais ao mesmo tempo e vc tem 201 reais na conta. simultaneo mesmo. provavelmente vc vai atender aos dois requests e, quando ficar negativo e o cara náo puder ficar negativo mete uma multa nele.

mas ai vc não quer que isso aconteça. vc vai ter que implementar algum recurso de “lock” para evitar problemas de simultaneidade. Por exemplo, vc pode gerar um Token de autorização por conexao sendo que o Token que vale é sempre um, que seria salvo em uma estrutura de dados xpto. na hora de responder ao saque vc pode ver se o Token é valido e como só pode haver um… no fim tudo é uma questão de “quem quer rir tem que fazer rir”.[/quote]

Entendo e respeito seu ponto de vista, mas… acho que eu só partiria para uma abordagem desse tipo se houvesse uma ótima razão e para mim um design mais simples por si só não seria uma razão boa o bastante, pois a simplicidade está nos olhos do observador (e aos meu olhos eu ainda não tenho certeza que manter checkpoints e undos seria um design realmente mais simples). TPM alto e particionamento seriam boas razões para desprezar o controle transacional oferecido pelo banco e implementar outro na unha.

Também nunca entendi como Event Sourcing se diferencia da simples implementação do pattern Command na camada de Domínio. Preciso ler mais sobre o assunto.

Ainda não consegui resolver essa questão, e estou começando outro projeto que já vi que vou ter o mesmo problema, então quem puder dar uma dica, agradeceria :slight_smile:

Pois meu gerenciador de entidades vai ser injetado por um container de DI, mas eu preciso ter a instância dele para controlar minhas transações, esse é meu problema.

Se você gerenciar suas transações nos Repositórios, como vai gerenciar uma transação que envolva mais de um Repositório? Eu acho que transações devem ser gerenciadas por serviços, usando anotações.

[quote=esmiralha]Se você gerenciar suas transações nos Repositórios, como vai gerenciar uma transação que envolva mais de um Repositório? Eu acho que transações devem ser gerenciadas por serviços, usando anotações.

Até onde entendi do tópico, tambem acho isso.

[/quote]

[quote=j0nny]Mas na minha arquitetura, não tenho service, é só assim:

Controller - Entidades - Repositorios [/quote]

Penso que deveria haver serviços ou algo similar neste ponto.

flws

Ok, assim é simples pensando em algo como JTA, Spring, mas se eu for gerenciar na mão?

Desculpem a insistência, mas alguém teve uma situação parecida?