Juntar repository e collections é possível?

[quote=Fabio Kung][quote=sergiotaborda]Quanto mais desacoplado for a entidade do repositorio melhor.

[code]
// opção de não usar collectino em turma

Turma {

List<Aluno> getAlunos(){
return AlunosRepository.getAlunos(this);
}

addAluno ( Aluno aluno) {
AlunosRepository.addAlunoTurma(this, aluno);
}
}
[/code][/quote]
Você não percebe que esse seu repositório não tem NADA de diferente de uma Collection?
[/quote]

Ok. Tente então serializar um e o outro e veja o que acontece.
Tente usar em ambiente distribuido e veja o que acontece.

Um repositorio tem as caracteristicas básicas de uma collection ( esse é o objetivo ! God Damm it!!)
Mas uma collection não tem as carateristicas de um repositorio : conhecimento do dominio.

[quote=Edufa]
Lendo as observações, tentei montar algo mais elaborado, não sei se peguei bem o espirito.

Se quizesse mudar para Hibernate e/ou JDBC, teria de implementar HibernatePersistenceStrategy ou JdbcPersistenceStrategy.
O problema cai no Strategy. Minha idéia seria usar um Filter (na falta de nome melhor e para não confundir com Criteria do hibernate), onde pudesse montar as restrições, e o PersistenceStrategy converteria esse Filter para a linguagem correta Filter -> Criteria ou Filter -> HQL ou Filter -> AnsiSQL

Seria esse um caminho ou seria exagero?[/quote]

Esse é o caminho se vc quiser uma coisa bem feita.
Vc assume certas coisas ai no exemplo, o seu repositorio é apenas um delegador para a estratégia de persistencia.
Não é só isso não. Esse seria o repositorio padrão, mas tem que ser possivel desenvolver um repositorio mais sofisticado. Vc tb assume que a estratégia já ai retorna o objeto final. Isso pode não ser assim. Parta do principio que o objeto da estratégia é um e o de dominio é outro. No caso particular eles podem ser o mesmo, mas não necessáriamente.
Turma tem que ter uma lista de alunos. Ela não pode delegar ao repositorio. Isso é gambiarra.
Lembre-se que pode haver serialização e distribuição dos objetos. Se a turma não mantém uma lista acoplaca a si mesma pode acontecer que quando aquele getalunos() é executado no servidor a lista não é a mesma porque outro usuário atualizou a lista de alunos. Isso deve gerar um exception. Ou seja, a turma tem que transportar consigo os alunos porque senão não ha como saber quais são.

enfim, a ideia é essa. Uma implementação dele e uso em casos reais vai mostrar que tem alguns pontos ingénuos. Mas isso é normal e só a implementação e uso fazem vc ver os detalhes.

Concordo, eu simplifiquei pq o dominio era simples, Ali mostra mesmo apenas o repositorio genérico. E o método list(Specification), mata muitas das consultas, a lógica fica dentro do Specification, mas é simplista para casos mais complexo.

Certo

ok, foi nesse ponto q eu me atrapalhei, não consegui ver como juntar os conceitos, esse ponto ainda está nebuloso.

[quote=sergiotaborda]enfim, a ideia é essa. Uma implementação dele e uso em casos reais vai mostrar que tem alguns pontos ingénuos. Mas isso é normal e só a implementação e uso fazem vc ver os detalhes.
[/quote]

Vivendo, errando e aprendendo, rs.
Vou ampliar o exemplo justamente para ver melhor os erros.

Eu andei vendo algumas implementações de criteria para jpa q até algumas q funcionam direto sobre o collections e o dominio, vc conhece alguma para indicar, para fazer a ponte do Filter -> Criteria q eu comentei acima?

[quote=Fabio Kung]Não sei se você está vendo neste caso, que não houve nenhuma necessidade de ter implementado da interface Repository estender a interface List. Você nunca referencia o repositorio como List. Sempre como repositorio.

Implementar List, não te trouxe vantagem alguma. Viu agora?

Eu não estou questionando sua implementação de persistência com Listas persistentes. O ponto é que fazer o Repositorio implementar List não está trazendo vantagem alguma.[/quote]

Entendi agora, Fabio. Realmente, não existe a necessidade de juntar repositório e coleções.

Mas ainda assim to gostando da idéia de Collections persistentes, Collections que também contenham regras específicas do meu domínio.

Não sou muito fã de manter os alunos de todas as turmas sincronizados com o repositório. Isso ao meu ver aumenta muito a complexidade. Modelar os seus repositórios prevendo todos os tipos de mecanismos persistência que podem ser conectados a ele, para mim, parece trabalho desnecessário.

Por exemplo, quero listar todos alunos reprovados independente das turmas:

Agora quero todos alunos reprovados de uma turma:

Se tenho as opções acima, para que esta próxima opção?

Resumindo, muito do esforço que é feito em um repositório poderia estar em uma coleção específica para tal domínio.

Para fazer update de um aluno, poderiamos fazer isso:

turmaRepository.getTurma(1).getAlunos().add(aluno) ou turmaRepository.getTurma(1).addAluno(aluno)

ao invés de

// poderia rolar um aluno.setTurma(turma); AlunoRepository.add(aluno);
Esse último código ao meu ver é praticamente um DAO. Acabei de matar a OO. A não ser que minha regra negócio permita que eu tenha alunos associados a nenhuma turma.

Sair criando reposiories genéricos para tudo quanto é entidade para mim vai dar em merda. Repositores com método add ou remove só justifica se ele é o topo da hieraquia de relacionamentos. No nosso exemplo, seria a classe Turma. Então, TurmaRepository.add e TurmaRepository.remove tá justifica, mas AlunoRepository.add e AlunoRepository.remove não.

Nem eu. Ainda bem que não foi isso que eu disse… é exactamente ao contrário.
Quando vc faz um Repositorio.getTurma() vc está trazendo um objeto turma totalmente desconexo de qualquer coisas. É um objeto simples com seu grafo de objetos e pronto.

O problema é que vc está assumindo que esse tipo de métodos são colocados no repositorio. Não são.
Porquê não são ? Tente responder a isso e vai descobrir que o repositorio não faz nada disso. Logo, o seu argumento é falho.

Vc está assumindo que sempre que eu adiconar um aluno à turma ele é automaticamente adicionado no banco/repositorio. Isso não é verdade. O grafo de turma ainda tem que ser validado, tem que passar por serviços e só no fim, se tudo estiver ok, que o aluno novo será adicionado à lista de alunos.
Por outro lado, vc não pode adicionar alunos do nada. Primeiro vc tem que obter o aluno para depois o adicionar à turma. Logo, o aluno até já existe no repositorio. Apenas a associação à turma é que não existe.

O que vc está procurando é uma linguagem fluente para fazer crud.
O problema é que ela é desnecessária porque crud não se faz assim.

@#$@#$ ele É o topo da hirarquia!

[quote=sergiotaborda]O que interessa é que vc SEMPRE vai poder usar o modelo na sua forma pura sem recurrer a truques ou a métodos “globais” como repositorio.getAlunosDa(turma).
Usar este tipo de codigo é fazer modelos orientados a banco de dados e não modelos OO.[/quote]
Eu nunca defendi isso. Apenas enumerei as opções. Minha opção sempre foi por fazer turma.getAlunos() e turma.getAlunosComNotaMenorQue(double).

Além disso, o seu jeito de fazer que me cheira procedural e não OO. Isso que você chama de usar o modelo na sua forma mais pura?

[quote]Para descobrir os alunos com nota maior que 8 não é necessário um repositório inteligente. Apenas um serviço que faça esse processo. E funciona ± assim:

Service {

  public List&lt;Aluno&gt; alunosNotaMaior(Disciplina d , int nota){
 
     RepositorioAlunos rep= ...  
     List&lt;Aluno&gt; alunos = new LinkedList(rep.getAll());

     for ( Iterator it = alunos .iterator(); it.hasNext();){
                 Aluno aluno = it.next();
                 if ( ! (aluno.getNota ( d )  &gt; nota )){
                          it.remove();
                 }
     }
      return alunos;
  }
}

[/quote]
Esse é um exemplo claro de que o mecanismo de persistência que você usa impacta sim, onde é ou não viável usar um repositório. Para qualquer sistema que não seja um pet-project é proibitivo fazer o que você fez.

Se eu sei que tenho um banco de dados, não há porque ignorar isso e trazer todos os objetos para a memória para filtrar alguns e pegar só o que eu quero. Você não convence ninguém a eliminar a principal vantagem dos bancos de dados relacionais, que é fazer consultas.

Ignorar o mecanismo de persistência é nivelar por baixo.

E aceitar o fato de que o banco de dados existe, não significa fazer sistemas orientados a banco de dados. Significa mesmo, apenas aceitar que ele existe e aproveitar aquilo que ele tem a te oferecer. Continuo podendo fazer DDD e sistemas OO.

DAOs não servem para isolar nada. Na prática isso nunca foi verdade. DAOs servem sim para centralizar o acesso aos dados e dividir a responsabilidade no seu sistema.

Daos servem pra dividir responsabilidade de q? Mas DAO’s nao são apenas Data-Mappers? Que deveriam ser plugáveis?

Edufa, perfeito o seu auto-questionamento.

A sua proposta é uma das melhores formas de se abstrair ao máximo os mecanismos de persistência, com Repositorios e Specifications.

Eu só discordo um pouco de deixar a responsabilidade de montar as Specifications pelo sistema todo. Eu as centralizaria no repositório mesmo e não exporia o método List getAlunos(Specification specification) para quem usa o repositório.

A questão que eu sempre faço a mim mesmo é: vale mesmo a pena colocar mais uma camada em cima do hibernate? Como o Laércio já postou, frases do próprio Evans:

Abstrair totalmente a persistência tem um custo muito alto e você vai ter que replicar muita coisa do que já está feito na API de Criteria, por exemplo. Aliás, Criteria é um bom exemplo do padrão Specification.

Além disso vai ter que fazer implementações de Specification diferentes para cada mecanismo diferente de persistência que tiver, ou então diferentes conversores (Specification -> HQL, Specification -> SQL, Specification -> XPath, Specification -> Criteria, …), que nada mais são do que verdadeiros compiladores.

Vale a pena? Quantas vezes alguém precisou trocar completamente o mecanismo de persistência?

Eu diria que não vale a pena.

Além disso, fatalmente você cai em algum caso que não dá para generalizar a operação específica de alguma das estratégias e aí você deixa ela de fora do seu conjunto de Specifications. Se conseguíssemos mesmo generalizar todo e qualquer mecanismo de persistência, não haveria necessidade de várias abordagens para consulta (relacional, oo, olap, document-based, …).

Daos servem pra dividir responsabilidade de q? Mas DAO’s nao são apenas Data-Mappers? Que deveriam ser plugáveis?[/quote]
Daos centralizam o código de acesso a dados para que você não fique fazendo select/hql/criteria/buscaemxml/… pelo seu sistema inteiro. E sim, eles são DataMappers.

[quote=sergiotaborda][quote]
Sair criando reposiories genéricos para tudo quanto é entidade para mim vai dar em merda. Repositores com método add ou remove só justifica se ele é o topo da hieraquia de relacionamentos.
[/quote]

@#$@#$ ele É o topo da hirarquia! [/quote]

Ok Sérgio, de qualquer maneira, não escrevi tudo aquilo com intenção de dizer que você disse o contrário. Mas eu reforcei essa colocação por que em um exemplo anterior você colocou um código onte o repositório de turma estava também mantendo o repositório de aluno.

[quote=sergiotaborda]
O codigo de repositorio de turmas é mais ou menos assim:

[code]class RepositoryTurma (){

Collection turmas = new LinkedHashSet(); // aqui que fica a coleção ( pq um Set?)
RepositoryAluno repAluno;

public void addTurma(Turma turma){
this.turmas.add(turma);
for (Aluno aluno : turma){
repAluno.add(aluno);
}
}

}
[/code][/quote]

Acho muito trabalhoso meus repositorios conter este tipo de lógica. Além de modelar o domínio também vou ter que pensar em como o meu repositório vai manter o domínio? Faz mais sentido o TurmaRepositorio saber que todos os alunos do sistemas estão espalhados pelas instâncias das turmas. Pronto! Para que manter um AlunoRepository?

Perfeito! Se eu entendi, então a validação pode ocorrer no método TurmaRepository.add, correto? A validação do aluno pode ocorrer na coleção, quando for feito turma.getAlunos.add/turma.addAluno.

O problema desta minha abordagem mesmo é ficar sobrescrevendo as collections. Isso pode acabar ficando trabalhoso, mas posso criar um grupo de collections que facilitem minha vida.

Afinal, qual é o problema das minhas collections também possuirem regras de negócio e saberem delegar ações para uma estratégia de persistência? ao meu ver, isso parece um recurso muito poderoso -> a minha infraestrutura pode injetar no meu domain collections conhecidas apenas por ele (a infra) sem que o domain tome conhecimento disso.

E não conhecem objetos de dominio certo?
Um DAO nao pode, por exemplo, retornar um List… senao como ele seria plugável?

[quote=Fabio Kung][quote=sergiotaborda]O que interessa é que vc SEMPRE vai poder usar o modelo na sua forma pura sem recurrer a truques ou a métodos “globais” como repositorio.getAlunosDa(turma).
Usar este tipo de codigo é fazer modelos orientados a banco de dados e não modelos OO.[/quote]
Eu nunca defendi isso. Apenas enumerei as opções. Minha opção sempre foi por fazer turma.getAlunos() e turma.getAlunosComNotaMenorQue(double).

Além disso, o seu jeito de fazer que me cheira procedural e não OO. Isso que você chama de usar o modelo na sua forma mais pura?
[/quote]

Para mim, modelo não é banco de dados. modelo são classes interagindo.
Se vc conhece uma forma mais pura, diga. Somos todos ouvidos.

[/quote]

Na prática vc tem razão. É poibitivo por motivos de arquitetura e limitação tecnologica. Mas não porque o modelo está mal desenhado ou existe algum problema com o conceito de repositorio.
O ponto era destingir a função do repositorio da função de um serviço. Eu poderia implementar de outro modo usando o padrão QueryObject. Mas eu só faria isso por motivos de limitação. Em tese o codigo acima não tem qualquer problema e reflete exactamente o que é necessário ser feito. E isso é a fiolosofia do DDD.

Pegue o exemplo de uma estratégia de persistencia em memoria como já foi exemplificado aqui.
Pense que ha um prevayler ou algo ali que não é um banco de dados. A $%#$% dos objetos está em memoria. O ponto é que vc não sabe. Não lhe interessa. Não é responsabilidade do dominio saber isso. Mesmo assim vc precisa do QueryObject porque fazer for a todo o momento não é prático. Contudo, o QueryObject para o caso da estratégia em memoria vai acabar sendo isso mesmo. E agora vc vai dizer que isso é irreal.
Não, não é irreal. Se vc tiver um ambiente distribuido onde precisa de cache local fazer essas pesquisas não é tão simples assim e vc não pode chamar o banco porque vc nem tem permissão de se conectar com ele!
Na prática vc usa o QueryObject e faz o seu DAO mapear esse objeto para uma forma interna de procura. No caso de objetos em memoria vc usa o for com um if , para o caso do XML usa XPath, para bancos usa SQL e assim vai. Mas quando vc cria o serviço de DOMINIO vc não pode estar pensando no SQL. Esse é o ponto.

Enquanto vc não enxergar o banco como “um mecanismo entre os possiveis” e não como deus, vc nunca vai sair do mesmo ponto.

O que eu fiz foi ignorar o mecanismo de consultas como vc disse no primeiro paragrafo e não o de persistencia.

Eu estava apenas tentando diferenciar a responsabildiade do repositorio da de um serviço. mas tudo bem. não entenderam ? dane-se.

Na prática vc implementa o padrão QueryObjet junto com Intrepreter (isto é o que os Criteria do hibernate são). E isso é que é funcional. contudo, quem monta a query ?? A @#$@#$ do serviço e não o repositorio.
Era só isso que eu estava dizendo.

Se vc sabe tão bem assim que tem um banco de dados esqueça repositorios. Não são para vc. A sério.
Repositorios são para pessoas que não querem saber se têm bancos de dados ( OO = encapsulamento = não quero saber).
Se os DAOs não servem para isolar nada para quê usá-los ? Use o hibernate directamente e seja feliz.
E não me diga que é daqueles que cria um DAO para cada entidade e depois chama o hibernate lá dentro.
Nem me diga que cria um Façade para o Hibernate. Estas coisas sim, não servem para nada.

Existe uma tempo em que estamos modelando. Falando em abstrato. Discutindo conceitos.
E existe um tempo em que estamos falando de codigo, implementação. Discutindo opções práticas.
Não pode misturar as duas coisas. Só serve para se confundir.

Concordo, eu acabei me influenciando por um projeto onde eu tive de montar filtros plugáveis, o usuário montava a consulta usando um monte de parametros e possibilidades, e salvava-as para serem reutilizadas.

Resumidamente ele montava as Criteria e agrupava em Specifications.

Nesse caso eu teria de externar a criteria e as specification, que na época não chamei por estes nomes pois não tinha esse conhecimento. Deu trabalho e ficou feio, mas bem flexivel para o usuário final. rs.

Seria uma abordagem com bastante poder em termos de consultas, mas algo bem específico.

Concordo totalmente. Acredito q como não existe solução simples recai-se num purismo de querer eliminar o Criteria do hibernate no repositório

Sim apesar de ver algumas apis q fazem isso, nenhuma delas é completa seria ótimo se tivesse algo como a Criteria do hibernate, só que de mais alto nivel q inclusive pudesse ser usada em collections. Deixando essa parte bem mais transparente.

[quote=Fabio Kung]
Vale a pena? Quantas vezes alguém precisou trocar completamente o mecanismo de persistência?

Eu diria que não vale a pena.

Além disso, fatalmente você cai em algum caso que não dá para generalizar a operação específica de alguma das estratégias e aí você deixa ela de fora do seu conjunto de Specifications. Se conseguíssemos mesmo generalizar todo e qualquer mecanismo de persistência, não haveria necessidade de várias abordagens para consulta (relacional, oo, olap, document-based, …).[/quote]

Acho interessante ver o lado purista, para depois ver o lado prático, comparar os dois e ver quais as melhores práticas que podem ser usadas. Se dá para montar algo genérico, mas ficar um monstro, legal, é interessnate ver o custo de certas escolhas. Até mesmo para poder defender onde e quando deve-se ser mais rigoroso.

Como apontei lá em cima eu tive de fazer uso de uma solução que ficaria muito mais elegante usando Specification e Criteria, desconhecer o caminho mais purista atrapalha, bem como aceita-lo sem questionamento.

perfeito edufa. Nesse seu caso eu também optaria por expor algum tipo de Specification.

Era só um exemplo!!! Na realidade o repositorio não vai chamar o repositorio de aluno porque o aluno já foi incluido antes. Era apenas um exemplo que tentava mostrar que a logica de um repositorio não é apenas delegar. ao DAO. O objetivo do respositorio é poder escrever acções sobre a coleção total de objetos de forma dependente do dominio. Se vc não precisa disso, vc nem precisa de repositorios.

Não.
Não vou explicar de novo a diferença entre consistencia e validação. A validação ocorre em objetos de vaidação especialmente construidos para isso.

Cara, tente implementar isso. Use um dominio sério e complexo.
Rápidamente vc vai descobrir os pros e contras dessa sua ideia.

[quote=sergiotaborda][quote=Fabio Kung]
(…)
Esse é um exemplo claro de que o mecanismo de persistência que você usa impacta sim, onde é ou não viável usar um repositório. Para qualquer sistema que não seja um pet-project é proibitivo fazer o que você fez.
[/quote]

Na prática vc tem razão. É poibitivo por motivos de arquitetura e limitação tecnologica. Mas não porque o modelo está mal desenhado ou existe algum problema com o conceito de repositorio.[/quote]
Ótimo Sérgio. Concordo plenamente com você aqui.
Só acho que não concordamos no que eu andei dizendo em resposta a outras pessoas, que eu acho que o custo é alto demais para se pagar e você não acha. Mas tudo bem, temos opiniões diferentes e ponto. A discussão foi válida.

Discussão encerrada?

[quote=fabiocsi][quote=Fabio Kung]
Daos centralizam o código de acesso a dados para que você não fique fazendo select/hql/criteria/buscaemxml/… pelo seu sistema inteiro. E sim, eles são DataMappers.
[/quote]

E não conhecem objetos de dominio certo?
Um DAO nao pode, por exemplo, retornar um List… senao como ele seria plugável?[/quote]

Exactamente. Não só isso como eles seriam dependentes do modelo não sendo reaproveitáveis.
O DAO tem que abstrair a API de persistencia, mas também tem que ser reutilizável em diferentes dominios.

Na prática se o objeto seguir certas convenções ( como ter um construtor publico sem parametros) vc pode usar Generics e Reflection para aceitar e devolver qualquer objeto. É um truque. Na realidade ele trabalha como Object.
Qualquer Object.

[quote=sergiotaborda] Cara, tente implementar isso. Use um dominio sério e complexo.
Rápidamente vc vai descobrir os pros e contras dessa sua ideia. [/quote]

Comecei a implementar algumas coisinhas. To começando com um modelo mais simples e vou adicionar complexidade aos poucos. Se eu for fiel a este ‘pet project’ e der uma enroscada (ou estiver fácil demais) eu volto aqui pra vocês colocarem lenha na fogueira. :mrgreen: