Transação em Repository

25 respostas
j0nny

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?

25 Respostas

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.

j0nny

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.

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.

Y

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.

j0nny

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.

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

Controller - Entidades - Repositorios

Oenning

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

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

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.

j0nny
Oenning:
Acho que seu código deveria ser assim, não?
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);
	}
}

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.

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

Oenning

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

j0nny

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

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

Oenning

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:

public class MeuRepositorioSqlServer : MeuRepositorio {
    private Session session;

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

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.

j0nny

É 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?

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.

j0nny

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.

Legal essa abordagem, vou dar uma estudada.

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

esmiralha

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

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.

j0nny

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.

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.

fantomas

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.

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

Controller - Entidades - Repositorios

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

flws

j0nny

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

j0nny

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

zica

j0nny, pelo que sei do assunto, quem deveria gerenciar seu controle transacional seria sua camada de aplicação:

The application layer is responsible for driving the workflow of the application, matching the use cases at hand. These operatios are interface-independent and can be both synchronous or message-driven. This layer is well suited for spanning transactions, high-level logging and security.

The application layer is thin in terms of domain logic - it merely coordinates the domain layer objects to perform the actual work.
http://dddsample.sourceforge.net/architecture.html

The application services in the sample application are responsible for driving workflow and coordinating transaction management (by use of the declarative transaction management support in Spring). They also provide a high-level abstraction for clients to use when interacting with the domain. These services are typically designed to define or support specific use cases. See BookingService or HandlingEventService.

In some situations, e.g. when dealing with graphs of lazy-loaded domain objects or when passing services’ return values over network boundaries, the services are wrapped in facades. The facades handle ORM session management issues and/or convert the domain objects to more portable Data Transfer Objects) that can be tailored to specific use cases. In that case, we consider the DTO-serializing facade part of the interfaces layer. See BookingServiceFacade for an example.

Como citado acima, seria um service, mas não um Domain Service, e sim um Application Service.

Acredito que essa seja uma saída válida, por exemplo, se em algum cenário você tivesse que alterar dois Aggregates diferentes em uma mesma transação.

fabioFx

cara, numa aplicação que exige essa complexidade de controle de transação, não usar um framework como o spring + AOP é meio arriscado.

como alternativa pro spring, o Guice seria algo mais “leve” :slight_smile:

mas acredito que com AOP vc resolveria facil…

mas de qualquer forma, no caso do DDD, o ideal é controlar transações apartir da camada de aplicação.

Andre_Brito

fabioFx:
cara, numa aplicação que exige essa complexidade de controle de transação, não usar um framework como o spring + AOP é meio arriscado.

como alternativa pro spring, o Guice seria algo mais “leve” :slight_smile:

mas acredito que com AOP vc resolveria facil…

mas de qualquer forma, no caso do DDD, o ideal é controlar transações apartir da camada de aplicação.


Então ele teria
Controller -> Application -> Entity -> Repository?

fabioFx

muitos consideram a camada de controle como parte da application, outro consideram que application está dentro do controller…

pra mim as duas são a mesma coisa… hehe

mas a idéia do teu fluxo está correta… mesclando o controller e o application.
eu pessoalmente colocaria entre o application e o entity uma classe Service (domain), no caso do dominio do problmema q vc exemplificou…

luistiagos

pq não poem um Facade no meio e gerencia as transações atravez dele?

fabioFx

mas esse é o objetivo do Service, mas quem gerencia a transação é a Application…

Andre, e por curiosidade, vc ta usando hibernate/jpa na impl. do repository?

Criado 6 de fevereiro de 2011
Ultima resposta 15 de jun. de 2011
Respostas 25
Participantes 10