Repositories dentro de Entities ou Repositories manipulados por Services?

Olá Pessoal,

Sei que já foi discutido muito aqui, algo que já gerou em torno de 8 páginas por tópico,
mas não ficou claro para mim.

Como o título descreve, aonde eu coloco os Repositories, dentro de Entities ou manipulados por Services?

Assim:


public class Categoria implements Serializable {
	
	private static final long serialVersionUID = 4224466529546255127L;
	
	private long idCategoria;
	private String descricao;
	private CategoriaRepository categoriaRepository; //Interface

	public Categoria saveCategoria(Categoria p_categoria){
		return categoriaRepository.save(p_categoria);
	}

//Get's and Set's
...
	
}

Ou partindo do princípio que devo modelar meu domínio, e que persistência é uma necessidade da infra estrutura
Tenho uma Service assim:


public class CategoriaServiceImpl implements CategoriaService {

	private CategoriaRepository categoriaRepository;//Interface

	public Categoria save(Categoria p_categoria) throws CategoriaException {
		p_categoria.valida();
		this.categoriaRepository.save(p_categoria);
		return p_categoria;
	}

...
}

Se a segunda abordagem estiver errada, por favor me mostrem as falhas!

Abraços!
\o/

Essa método ai é um problema, Vc o usaria ± assim


Categoria c = ... // otbem uma categoria de algum lugar 
// faz alguma coisa com ela

c.saveCategoria(c);

O que é redundante.Opções:

  1. tornar o método sem parametros com codigo usando this: isto equivale a tornar Categoria num ActiveRegistry. Nada contra se vc fizer assim. Eu não faria.
  2. torna o metodo estático. Isto equivale a torna Categoria um façade do seu proprio repositorio.
    O que não deixa de ser um pouco redundate.
  3. eliminar o método e usar o repositorio directamente

Categoria c = ... // otbem uma categoria de algum lugar 
// faz alguma coisa com ela

CategoriaRepository cr = //... obtém de algum lugar
cr.save(c);

A segunda abordagem parte do principio que vc está usando a opção 3 da lista anterior.
Se vc usar algumas das outras o codigo será diferente.
O ponto é que vc não amarrou a entidade com o seu repositorio e precisa perder tempo
procurando o repositorio certo, ou vê-lo injectado.

O codigo do serviço não tem nenhum problema (além de falta de separação de responsabilidade em categoria.valida(). Mas isso é uma escolha que nada tem a haver com repository.)

Nada impede se construir algo mais sofisticado.


// no inicio da aplicação
RepositoryRegistry.registry ( Categoria.class, CategoriaRepository.class);

// em algum outro lugar da aplicação

RepositoryRegistry.save(c);  (1)

(1) Assume que todos os Repository tem a mesma interface básica. Por exemplo save(Object obj). Isso não
é dificil. De resto é apenas um mapa do tipo Map<Class, Repository> e o codigo é muito simples


class RepositoryRegistry {

Map registry = new HashMap();

registry ( Class c, Repository rep);
        registry.put(c,rep);
}


save( Object c);
     Repository rep = (Repository)registry.put(c.getClass();
 rep.save(c);
}

É claro que existem outras formas de amarrar a classe ao seu repositorio mas
esta é bem simples.

Em adição como está programando para interface o repositorio real pode ser sempre o mesmo.

Olá,

Então estou usando o Spring Framework para injetar as depêndencias na segunda abordagem ficaria algo assim:

ServiceCategoria


@Service(value="categoriaService")
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=CategoriaException.class)
public class CategoriaServiceImpl implements CategoriaService {

	private CategoriaRepository categoriaRepository;

	@Autowired
	public void setCategoriaRepository(CategoriaRepository categoriaRepository) {
		this.categoriaRepository = categoriaRepository;
	}

	public Categoria save(Categoria p_categoria) throws CategoriaException {
		try {
			p_categoria.fazAlgo();
			...
			this.categoriaRepository.save(p_categoria);
			return p_categoria;
		} catch (CategoriaException e) {
			throw new CategoriaException("Não foi possível salvar." +e.getMessage());
		}catch (Exception e) {
			throw new CategoriaException("Ocorreu um Erro desconhecido." +
					"\nDetalhes Técnicos: "+e.getMessage());
		}
	}

O valida aquela hora foi simbólico, apenas quero demonstrar que antes de aplicar o objeto a um repositório, aplico os comportamentos ao que for necessário, como por exemplo em uma Service de um Financeiro, ficaria algo assim:

ServiceFinanceiro

...
finaceiro.calculaDispesas();
...
repositoryFinanceito.save(financeiro);

Uma recuperação de lista, eu iria fazer uma Classe do Tipo service acessar um repository como exemplo:

ServiceCategoria


public Categoria findById(Categoria p_categoria) throws CategoriaException {
		try {
			
			return this.categoriaRepository.findById(p_categoria);
			
		} catch (CategoriaException e) {
			throw new CategoriaException("Não foi possível procurar pela ID."+e.getMessage());
		}catch (Exception e) {
			throw new CategoriaException("Ocorreu um Erro desconhecido." +
					"\nDetalhes Técnicos: "+e.getMessage());
		}
	}

	public List&lt;Categoria&gt; getList() throws CategoriaException {
		try {
			
			return this.categoriaRepository.getList();
			
		} catch (CategoriaException e) {
			throw new CategoriaException("Não foi possível listar."+e.getMessage());
		}catch (Exception e) {
			throw new CategoriaException("Ocorreu um Erro desconhecido." +
					"\nDetalhes Técnicos: "+e.getMessage());
		}
	}

Então, é correto desaclopar o Repository de um Entity e fazer com que Service manipule o Respository?

O catch de CategoriaException é redundate. Remova-o.

não faça isso. Use o repositorio directamente. Caso contrário vc está criando fachadas para o repositorio sem nenhum propósito.

[quote]
Então, é correto desaclopar o Repository de um Entity e fazer com que Service manipule o Respository?[/quote]

O service usar o repositorio directamente não tem problema algum.
O entity usar o repositorio de outros entity tb não. O que é estranho é o entity usar o seu proprio repositorio apenas para delegar. Faria sentido se o entitity tem caracteristicas de nodo de arvore ( por exemplo Tarefa que contém outras Tarefa). Não é proibido o entity usar o repository, mas tem que o fazer com algum objetivo mais “elevado” que simplesmente delegar. Caso contrario estamos criado Façade ou ActiveRecord sem nenhum ganho.

As duas abordagens estão “corretas”. A coisa mais importante é que repositório não tem a ver com persistência, pelo menos não diretamente. Um bom exemplo disso é que se você usasse um DAO diretamente ao invés do Repositório no primeirot recho teria algo arecido com um Active Record, onde a persistência do objeto fica a cargo dele mesmo 9que pode delegar para o DAO) mas quando é um repositório isso não é verdade, o objeto não se persiste ou consulta o banco de dados, ele consulta o repositório e a implementaçao deste é opaca à Camada de negócios.

Olá,

Não sei se fiz correto, mas abaixo da service, apenas são tratadas e disparadas Exceptions do Tipo CategoriaException, logo esse catch exibi uma mensagem formatada.

Aproveitando, gostaria de saber se vocês tem uma referência para tratamento de exceptions, e usando AOP para as exceptions corriqueiras.

Hun… não sei conseguiria fazer isso, prefiro delegar pelo menos uma facade para minhas layers abaixo, seja para tratar exceptions e afins.
Outro detalhe, é que talvez antes de recuperar a lista, talvez seja necessário um categoria.fazAlgo(); e então recuperar os dados.

Ok! Shoes, apesar do “correta” o.O

Quanto a abstração do Repository, acredito que para mim está clara. Abaixo do Repository, tenho DAO’s seja do Hibernate ou JDBC, que implementam esta interface Repository, e a injeção da dependência é feita pelo Spring.

Obrigado a todos pela atenção.
\o/

[quote=rpffoz]Olá,

Não sei se fiz correto, mas abaixo da service, apenas são tratadas e disparadas Exceptions do Tipo CategoriaException, logo esse catch exibi uma mensagem formatada.
[/quote]

Exceptions não servem para formatar informação.
Quem faz isso é UI. Vc está caputrando uma CategoriaException e produzindo outra igual. Isso é desnecessário.
A informação do erro ja está na primeiroa exception. Se não está, deveria.

“Não foi possivel salvar” não contém informação nenhuma. É obvio que não foi possivel : uma exceção foi lancada. E analizando o stacktrace é obvio onde foi lançada e o quê “não pode acontecer”.
Escrever mensagens como esta na exception é inutil. Mas se o quiser fazer deve ser feito na camada de UI
por um formatador (Format) ou dentro de um ExceptionHandler.

Encapsular uma exception em outra de tipo diferente é lictio. Mas não de os tipos são iguais.

Não com AOP mas no meu blog tem algo sobre tratamento de exceptions.

Hun… não sei conseguiria fazer isso, prefiro delegar pelo menos uma facade para minhas layers abaixo, seja para tratar exceptions e afins.
Outro detalhe, é que talvez antes de recuperar a lista, talvez seja necessário um categoria.fazAlgo(); e então recuperar os dados.
[/quote]

Se e quanto vc sentir essa necessidade é porque vc não está mais utilizando o repositorio puro. E nesse caso tlv vc tenha que delegar a outro serviço ou incluir mais codigo no repositorio.
Tudo dependende da responsabilidade de cada objeto e não do que vc precisa fazer.
Se vc não quer repartir o que ha a fazer por quem o deve fazer então não precisava de OO em primeiro lugar.

Uma fachada que apenas delega é um empecilho.

Shoes,

Pense comigo: se vc usa um “service”, você possívelmente utiliza algum Spring da vida ou até mesmo EJBs para instanciar os seus “services”. Ou seja, você tem em um ponto de entrada “único” para a camada de apresentação chamar a camada de negócios. Com isso, vc pode tirar proveito das facilidades do Spring ou EJB, por exemplo, Transaction Management, injeção do Entity Manager, e coisas desse tipo.

Se você utiliza o repository direto na “entity”, fica difícil tirar proveito dessas facilidades (eu imagino que a entity é geralmente instanciada com new e não via IoC\Lookup), ou pelo menos do tranasction management.

Nesse cenário, será que não é melhor utilizar um “service”?

Não sou o Shoes mas vou opiniar Microfilo.
Quando se usa Repositories, teoricamente sua modelagem deve estar sendo criada com base em Domain-Driven Design (pelo menos tende a ser).

Portanto o design é para o negócio e não para a arquitetura na camada de aplicação. Uma Façade qualquer pode isolar seu Domínio de Negócio do Domínio da Aplicação. Hoje em dia, nem mesmo isso é necessário, eu por exemplo faço isso usando o JbossSeam:

… diretamente em meu xhtml.

Em outros pontos da aplicação eu faço:

… onde o método na entidade Client faz acesso a seu repositório buscando as ultimas compras deste cliente.

Utilize o advice “around” com o AspectJ. Com ele vc consegue contornar qualquer pointcut que desejar com try/catch.

Então Lezinho… a questão é: quando se usa um Spring da vida ou EJB, você ganha algumas facilidades como Transaction Management, injeção do EntityManager (ou Hibernate Session/JDBC Connection), entre outras. Só que dificilmente você instanciaria um “Entity” com Spring ou EJB. Por isso você acaba ficando sem poder utilizar as facilidades deles diretamente do “Entity”. Por isso que eu acho que é mais negócio utilizar o Repository (ou DAO) no “service”, uma vez que esse provavelmente é instanciado pelo Spring\EJB e pode aproveitar das facilidades do framework diretamente em si mesmo.
Não sei se ficou bem claro…

As chamadas são server-side ou client-side?

(PS.: espero que os trolls fiquem LONGE dessa thread, ta com cara de que vai virar um debate muito interessante)

Entendi microfilo, mas mais uma vez o limite é tecnológico (utilizar facilidades do EJB/Spring).

Neste caso mais específico, o EJB pode ser a façade que mencionei e usar os controles transacionais necessários, ou até mesmo ser seu Repository (um EJB3 sendo a implementação de um repository não é ruim, muito pelo contrário).

De forma geral um repository pode estar dentro ou fora da entidade, conforme sua necessidade. Uma abordagem como entidade.salvar não me agrada nem um pouco, neste caso utilizo um repository fora da entidade. Porém a entidade como objeto de negócio que é, certamente irá precisar em algum momento de acesso a dados via repository, em seus próprios métodos, como o comportamento que citei em “client.latestPurchases”, neste caso uso um Repository interno.

Mas sei que isso pode gerar outros problemas, como:
“Mas como vou injetar Repositories em minha entidade se ela não é gerenciada pelo meu container IoC, é instanciada via new ou por frameworks de persistência?”

… bom, se sua dúvida for essa, eu postei uma solução aqui:

http://www.guj.com.br/posts/list/70275.java

Server-side, embora poderia ser client-side em vista que consigo invocar qualquer metodo mesmo com Javascript, bastando ele estar anotado por @WebRemote para esta ultima.

Acredito que na maioria dos demarcadores transcionais não podem ficar no repository.

Entendo Lezinho, mas em todo o caso, haverá momentos em que a “entity” precisara efetuar operações de update e, em alguns casos, precisara que essas operações estejam em transações isoladas. Como minha “entity” não é instanciada via spring\ejb, ela não possui demarcação de transação. E, como eu disse acima, não acho que seria correto demarcar a transação no repository, na maioria dos casos.

Você se refere ao esquema com AOP? Legal :slight_smile: Não estou acostumado a utilizar AOP capturando a execução do construtor. Será que da para fazer algum transaction management diretamente na “entity” utilizando AOP? Nesse caso, a entity não ficaria muito “parruda”?

[quote=Lezinho]
Server-side, embora poderia ser client-side em vista que consigo invocar qualquer metodo mesmo com Javascript, bastando ele estar anotado por @WebRemote para esta ultima.[/quote]
Bacana, bem parecido com DWR, não?

Concordo. Citei um Repository como EJB3 apenas para questão do uso de injeção convencional do EntityManager.

Em caso de transações isoladas em entidades para entidades poderia ser feito:

  1. Construir uma Strategy de um objeto de transação fake (para poder mudar a implementação se necessário). Esta strategy é um atributo injetado em sua entidade (portanto controlado pelo ciclo de vida do seu framework de injeção/transação). Ele tem um atributo que inicia uma nova transação isolada e que a finaliza. Você pode chamar este atributo nos pontos necessários de sua entidade.

  2. Criar uma anotação para novas transações na entidade. Funcionaria exatamente como descrevi acima, com a chamada para um objeto de transação sob o controle de seu framework, exceto pelo fato de você não ter que fazer um atributo a mais na sua entidade… mas sim anotar um método. Um "aspecto"deverá fazer a leitura para efetuar a transação.

  3. Fazer todo o processo transacional por fora, em um service. É a forma mais simples e direta, porém pode compromete um pouco o modelo.

Costumo utilizar controle de transação otimista, onde ela se inicia no começo de um request e finaliza no seu fim (via filtros).
Em caso de transações atômicas uso flushMode manual do hibernate, onde os DAOs (e não repositories) possuem métodos com flush explícito. Para o Repositories isso é transparente e não vaza para o domínio. Claro que o filtro é informado quando a transação é atômica ou não (na realidade isso é feito pelo Seam).

Eu acho que não fica ruim. Ela é parruda tão quanto o modelo assim o faz, eu acho justo.

[quote=microfilo]

Bacana, bem parecido com DWR, não?[/quote]

Na realidade bem mais transparente. Com a EL dele é possível invocar qualquer método de classes de negócio sem ter um Command por trás disso em qualquer página, e sem javaScript adicionais (é server-side). Mais se acaso vc tiver um JavaScript e precisar invocar um método de um objeto de negócio que esta em algum escopo, neste caso vc utiliza a anotação @WebRemote… somente isso.

Aí eu não tenho um mecanismo que gerencia a transação. Seria o mesmo que controlar a UserTransaction na mão. Da muito trabalho e obriga os meus objetos de dominio a lidar com transação, algo que eu não acho legal.

Talvez seja uma opção viável. Mas seria a “entity” o local mais adequado para demarcar as transações? e se eu for utilizar uma mesma “entity” em um outro contexto em que o isolamento transacional é diferente?

Depende, podemos fazer o service apenas cuidar de coisas de “infraestrutura”, como demarcação de transações e delegar a execução de regra de negócio á “entity”.

Acredito que isso é o suficiente na maioria dos casos. Mas existem as exceções. E nesse caso, não são tão raras…

O flush sem si não resolve… Mesmo que você faça o flush, se você fizer o rollback da transação, o seu update não sera refletido no banco de dados.

[quote=Lezinho]
Na realidade bem mais transparente. Com a EL dele é possível invocar qualquer método de classes de negócio sem ter um Command por trás disso em qualquer página, e sem javaScript adicionais (é server-side). Mais se acaso vc tiver um JavaScript e precisar invocar um método de um objeto de negócio que esta em algum escopo, neste caso vc utiliza a anotação @WebRemote… somente isso.[/quote]

Acho que a gente se confundiu, heheh. Para invocações client-side é bem parecido com DWR.

Exatamente, o controle é explícito programaticamente, embora a abstração da API transacional através da estratégia lhe garanta um Gateway no domínio isolado da tecnologia. Quanto ao domínio lhe dar com a transação, mesmo se você colocasse metadados nas entidades, como as annotations @Transaction, o controle continuaria no Domínio.

[quote=microfilo]
Mas seria a “entity” o local mais adequado para demarcar as transações? e se eu for utilizar uma mesma “entity” em um outro contexto em que o isolamento transacional é diferente? [/quote]

Transações são ações bem particulares dos casos de uso. Fatalmente, se você possui um domínio rico e ubiquitous, o comportamento da entidade reflete diretamente estas ações. Se um comportamento explícito em um ou mais métodos requer determinado tratamento, por consequência toda chamada a este método/comportamento deve ser executada de mesma forma, caso contrário é uma ação diferente.

Se isso estiver em um Service (o controle de transações), não muda nada. Services também são objetos de negócio pertencentes ao domínio, assim como Aggregates, ValueObjects, Entities, etc. Da mesma forma que você pode utilizar um Entity em lugares distintos, você também poderia utilizar um service. A questão é, se em lugares diferentes eles podem possuir comportamentos diferentes, então alguma coisa na modelagem pode não estar legal.

Claro, outra alternativa é deixar a cargo da Application Layer (Actions, Commands, ManagedBeans e afins). Em vista que a transação possa ser encarada como serviço do sistema, a própria camada de aplicação pode resolver este problema… a questão é, será que isso é interessante para todos os casos? Eu acredito que não.

[quote=microfilo]

O flush em si não resolve… Mesmo que você faça o flush, se você fizer o rollback da transação, o seu update não sera refletido no banco de dados. [/quote]

Isso é o que espero, note que me referi as transações atômicas e não em isolamentos.

Pois é, hehe. Mas com o DWR eu posso invocar diretamente um EJB na página?

[]'s

Desculpa, eu me perdi na troca de mensagens e não entendi alguma coisa que fez o resto todo perder sentido: estamos falando de criação de Entities ou de demarcação de transação?

Não há problema em demarcadores de transação ficarem em qualquer lugar, creio. Esse tipo de metadado -apesar de intrusivo- não influencia no domínio.

Que tal algo como:

  • Façade: demarca inicio da transação, recebe dados e passa para repositório.cria
  • Repositório.cria : exige uma trnasação já existente, cria o usuário

A discussão era sobre onde gerenciar a transação e como isso poderia se dar em situações onde o repositório esta diretamente na Entity e como fazer isso.

Resumidamente eu acho que depende da situação. Se existir façade, que se faça nela… se o processo se inicia em um Service, que seja nele… caso for na entidade, que seja nela.

Outra discussão foi de “como” fazer isso, quando o controle esta diretamente na Entidade…