Juntar repository e collections é possível?

57 respostas
Thiago_Senna

Olá,

andei pensando (ou viajando?) na possibilidade de juntar repository e collections. Por exemplo:

interface AlunoRepository implements List {

   List<Aluno> getAlunosReprovados(AnoLetivo anoLetivo);

}
class Turma {

  // atributos omitidos

  AlunoRepository alunos = RepositoryFactory.getAlunoRepository(this);

	
  public void addAluno(Aluno aluno) {
    alunos.add(aluno);
  }

  // outros metodos omitidos

  public List<Aluno> getAlunosReprovadosNoAnoAnterior() {
    AnoLetivo anoAnterior = new AnoLetivo(new Date().getYear() - 1);
    return alunos.getAlunosReprovados(anoAnterior);
  }


}

Gostaria de saber a opinião de vocês. Por exemplo: algo no DDD reprovaria essa idéia? É tecnicamente inviável? A dificuldade vale a pena? Estou agregando valor para o meu domínio?

Enfim, gostaria de saber a opinião do grupo :)

Até +

57 Respostas

Emerson_Macedo

Eu não gosto muito da idéia por alguns motivos:

   - você vai ter que implementar todos os métodos de List. Eu diria que nunca você vai precisar disso tudo.
   - você vai ficar tentado a implementar métodos que não façam nada só para obedecer o contrato.
   - Geralmente as operações significativas do seu Repository, exceto adicionar e remover não estão no contrato da interface List.
   - Outras pessoas que usarem esse repositório vão com toda vontade achando que faz tudo que uma List faz e irão se decepcionar.
   - Caso você decida realmente implementar toda a interface (que seria o correto), imagine o trabalhinho para implementar alguns métodos como por exemplo iterator() e seus primos próximos?
   - Quando você quiser usar os métodos que só estarão na implementação, terá que fazer uma chamada não-polimórfica.

Em fim, isso tudo se resume em uma coisa: Respeitar um contrato que para o cenário do Repository não faz muito sentido.

Apesar do conceito do Repository ser algo “como se fosse uma Collection”, seu contrato será diferente do da API Collections do Java, pelo menos IMO.

[]s

Thiago_Senna
emerleite:
Em fim, isso tudo se resume em uma coisa: Respeitar um contrato que para o cenário do Repository não faz muito sentido.

também pensei nestas possibilidades que você citou. Então, por enquanto, to pensando na possibilidade de respeitar o contrato feito com a interface. Por exemplo, esse código abaixo, apesar de não ser o ideal, já ajudaria:

abstract class AlunoRepository extends ArrayList{  
  
   abstract List<Aluno> getAlunosReprovados(AnoLetivo anoLetivo);  
  
}

A classe concreta por sua vez se encarrega apenas de implementar os métodos abstratos. Pode ser uma alternativa, não pode?

Emerson_Macedo

Continua com o mesmo problema pois você terá que sobrescrever todos os métodos. Caso contrário quem usar algum método que você não tenha sobrescrito obterá resultados não desejados.

Não é mais fácil você substituir essa herança por composição?

Thiago_Senna

Sim sim! Por isso no primeiro exemplo usei interface, para dar liberdade pra nossa imaginação :). Usei herança neste exemplo por praticidade. Na prática, optaria com composição.

Pode ser, mas e se eu conseguisse uma solução onde eu não precisasse sobrescrever nenhum método da interface Collection/List?

Por exemplo, no caso de uma composição, como você sugeriu, eu sobrescrevo todos os métodos da interface List para delegar para um ArrayList. Ou seja, meu repositório continua sendo na íntegra uma Collection. Mas é aqui onde começam a surgir as dificuldade técnicas, ao meu ver. O hibernate talvez consiga numa boa identificar que foi adicionado um item na collection, já a implementação usando jdbc seria bem mais punk e pode exigir que não nos limitemos em apenas delegar tudo para ArrayList. Ficou claro o meu ponto de vista?

Emerson_Macedo

Eu entendi o seu ponto de vista, só não vejo o que estamos ganhando com esse trabalho todo :wink:

Thiago_Senna

Talvez eu esteja exagerando. Mas o que estou fazendo é questionando o padrão Repository. Se a collection de alunos é um repositório ‘natural’ de alunos, por que ele não poderia aderir métodos específicos para acessar um centro de armazenamento de dados desacoplado? Ou seja, da forma que é implementado hoje você cria uma collection e um repositório. Essas duas coisas poderiam ser uma coisa só.

public void execute() {

turma = new Turma();
turmaRepository.add(turma);

aluno = new Aluno();
turma.addAluno(aluno);

}

ao invés de

public void execute() {

turma = new Turma();
turmaRepository.add(turma);

aluno = new Aluno();
turma.addAluno(aluno);
alunoRepository.add(aluno);
}

tudo bem que turma.addAluno() poderia adicionar aluno na collections e em seguida chamar alunoRepository.add, mas são duas chamadas com a mesma finalidade, não é?

sergiotaborda

Thiago Senna:
Olá,

andei pensando (ou viajando?) na possibilidade de juntar repository e collections.
Gostaria de saber a opinião de vocês. Por exemplo: algo no DDD reprovaria essa idéia? É tecnicamente inviável? A dificuldade vale a pena? Estou agregando valor para o meu domínio?

O padrão repository defini-se como "Acesso ao conjunto de instancias da entidade como se fosse uma coleção"
básicamente significa : “Acesso ao collection de X sem implementar Collection”

A ideia é que existam métodos como add e remove igual aos de Collection, mas que não tenham que existir métodos como toArray(), addAll() , retain(), etc…
Portanto, o proprio padrão está dizendo “Não faça repository implementar Collection”.
Por maioria de razão não faz sentido implementar List (os dados não são indexados por posição)
Em resumo: não vale a pena, não está agregando valor é mais dificil de implementar coerentemente e pior: vai contra o propósito do próprio padrão Repository.

le-silva

Também não acho legal não, Thiago. Concordo com o Emerleite.

Sugestão?

/**
 * Interface básica para repositório de objetos.
 * 
 * @author XPTO
 * 
 */
public interface Repository<E extends EntityObject> {

	/**
	 * Adiciona uma entidade ao repositório de objetos.
	 * 
	 * @param entity
	 * @throws RepositoryException
	 */
	public void add(E entity) throws RepositoryException;

	/**
	 * Atualiza uma entidade contida no repositório.
	 * 
	 * @param entity
	 * @throws RepositoryException
	 */
	public void update(E entity) throws RepositoryException;

	/**
	 * Remove uma entidade do repositório.
	 * 
	 * @param entity
	 * @throws RepositoryException
	 */
	public void remove(E entity) throws RepositoryException;

	/**
	 * Obtem um objeto da entidade contida no repositório, baseado no parametro recebido.
	 * 
	 * @param id
	 * @return
	 * @throws RepositoryException
	 */
	public E get(Object id) throws RepositoryException;
}

E nas interfaces que extenderem esta, você adiciona métodos específicos de listarBlaBlaBla, etc, etc, etc. Ou então, implementa um get ou list com um search criteria da vida nesta própria interface básica, e boa.

Sigo nessa linha… IMHO…

Abraço!

sergiotaborda

Perái… agora vc viajou. O que tem a haver implementar ou não collection com o adicionar o aluno explicitamente ao seu repositorio ?

O correto sempre será

turma = new Turma();
aluno = new Aluno();
turma.addAluno(aluno);

turmaRepository.add(turma);

Se a implementação especifica pode lidar com chamadas implicitas aos outros repositorios é outra historia.

Emerson_Macedo

É por isso que Repository é um Design Pattern :smiley: . Existe um problema reccorente que é o fato dos objetos não serem auto persistidos. A melhor forma que se encontrou pra resolver isso foi essa. Tinhamos o DAO, que na verdade é um DataMapper misturado com Repository IMO. Achou-se interessante criar esse conceito de Repository para fazer parte do domínio.

Esse problema que você disse é justamente porque a collection não é o DB. O fato é que o os objetos não sendo auto-persistidos, o Repository se tornou uma alternativa elegante para termos algo no nosso Domain Model.

Quando a Collection, sinceramente não acho essa implementação vá ajudar. Mesmo que você crie um adapter não acho que vá ganhar muita coisa. Ainda mais se falarmos de sistemas web onde ao final da requisição os objetos morrem e na próxima vai ter que chamar tudo novamente.

No seu caso, vai adicionar o Aluno a turma e dificilmente após essa operação você vai querer uma lista dos alunos daquela turma. Talvez no próximo request você precise mas ai de fato seu objeto Turma já se perdeu. O máximo que você vai ter é o ID da turma armazenado no cliente de alguma forma e vai passar isso ao servidor pra começar tudo denovo.

tnaires

Isso só acontece quando insistimos em colocar métodos de negócio dentro do DAO. Mas o DAO não foi feito pra isso.

Emerson_Macedo

Isso só acontece quando insistimos em colocar métodos de negócio dentro do DAO. Mas o DAO não foi feito pra isso.
AHM??? Pode explicar isso? Quem disse isso?

Thiago_Senna

Então fechado -> O padrão repository não é e nem pode ser uma Collection.

Tentarei continuar a discussão para essa abordagem que coloquei no início do tópico, mas consciente que Collections e Repository são coisas distintas. :wink:

sergiotaborda:
O correto sempre será

  1. turma = new Turma();
  2. aluno = new Aluno();
  3. turma.addAluno(aluno);
  4. turmaRepository.add(turma);

Se a implementação especifica pode lidar com chamadas implicitas aos outros repositorios é outra historia.

À partir desta colocação tentarei colocar minha insatisfação com repository: imagine o seguinte caso:

Turma turma = turmaRepository.get(turmaId); Aluno aluno = new Aluno(); turma.addAluno(aluno);

Se por baixo dos panos é hibernate por exemplo, esse código funciona que uma beleza. Se for JDBC por debaixo dos panos o jeito mais fácil de garantir que aluno será persistido é usar alunoRepository.add(aluno);

Ou seja, você ainda não está completamente desamarrado da tecnologia de persistência. Mas enfim, não sou eu quem vai resolver este problema, hehe. O que acho desperdício é o fato de que em turma.addAluno eu já fiz uma chamada em alunos.add. Qualquer coisa fora disso, ao meu ver, é gambi para alinhar OO e Tabela Relacional.

A Collections no fim são usados no domínio. Uma solução não poderia ser deixar elas um pouco mais espertinhas e torná-las mais específicas para o negócio?

Enfim, o que me parece é que já não estou mais falando de repositório, mas de uma opção a ele. Uma coleção específica para o negócio. (humm… to viajando!)

Só pra somar na discussão comecei a dar uma olhada em como poderia implementar isso no hibernate e achei este artigo: http://www.javalobby.org/java/forums/m91831280.html

Fabio_Kung

Oi Thiago,

Eu também não gosto muito da idéia por um outro ponto de vista:
Por que é mesmo que você precisa se referenciar a um Repositório como List? :wink:

Para você satisfazer sua tentação, apenas faça como eu. Chame o repositório de Collection sem implementar java.util.Collection, mas não conta para ninguém!

Thiago_Senna

Fabio,

no seu caso este seu repositório que você chama de collection também está servindo para definir que existe um relacionamento entre dois objetos, como no caso * Turma possui N Alunos*? Ou você ainda mantém uma collection demarcando a existência de um relacionamento?

Emerson_Macedo

Thiago, na verdade enquanto o problema tecnologico existir, continuaremos a ter que implementar essa “Gambi” se é que você me entende.

Fabio_Kung

Thiago Senna:
Fabio,

no seu caso este seu repositório que você chama de collection também está servindo para definir que existe um relacionamento entre dois objetos, como no caso * Turma possui N Alunos*? Ou você ainda mantém uma collection demarcando a existência de um relacionamento?


2a. opção. Mantenho a Collection, que para mim também é um repositório e eu não conto para ninguém. :smiley:

Mas depende do caso. Naquela discussão de ter um repositório dentro de entidades ou não, se você escolhe por deixar repositórios dentro de entities, o repositorio acaba servindo para definir relacionamentos complicados como: “1 Turma possui N Alunos transferidos de outros estados”.

class Turma {
  private final RepositorioDeAlunos rep;
  ...
  public List&lt;Aluno&gt; getAlunosTransferidosDeOutrosEstados() {
    return this.rep.getAlunosTransferidos(&lt;de&gt;, &lt;para&gt;);
  }
}
sergiotaborda

O problema é que vcs continuam pensando no Repositorio como um outro nome para DAO.
Repositorio não é isso. O repositorio é o cara onde as logicas de mapeamento entre entidades ficam.

O codigo de repositorio de turmas é mais ou menos assim:

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


}

Sabe quand vc faz um diagrama de classes e diz que Turma --contém–> Aluno ? Ora , o repositorio de turma conhece essa relação. Tão simples assim.

Não usei o DAO para reforçar o conceito de que Repositorio não é DAO. Não é.

Fabio_Kung

Sergio, isso ajuda bastante se você não usa nenhum mecanismo de orm. Usando um orm, eu não acho que compense o esforço de fazer isso quando um simples @ManyToOne resolve.

sergiotaborda

Cara, uma coisa é o conceito e a utilidade do repositorio. Outra coisa é ORM.
Misturar os dois dá merda.

O problema é que @manyToOne resolve em certas circusntâncias. Como já foi dito : se usar hibernate é tudo uma maravilha, mas se usar JDBC é um pesadelo. Mas Hibernate e JDBC são tecnologias de persistencia.
um repositorio (repositorio : onde vc coloca as coisas e depois pega de volta) não tem nada a ver com persistencia.
(persistir: manter por muito tempo)
Já existiram vários topicos sobre isto , e eu digo sempre a mesma coisa, então não vou me repetir. A questão é :

  1. Entender o papel do Repositorio. Entender que o repositorio serve escatamente para não existirem ActiveRecords. Entender que o repositorio é tão ou mais consciente das relações entre as entidades que a entidades elas mesmas.

  2. Entender que Repositorio é diferente de DAO. Eu sei que é dificil , mas ao menos tentem.
    Repositorio : prover acesso centralizado ao conjunto de instancias de uma entidade.
    DAO: prover acesso padronizado a N diferentes tencologias de persitencia cujas API não são equivalentes.

  3. Entender que se ( atenção ao SE) o sistema necessita de persistência então terá que haver um mapeamento entre o repositorio ( instancias em memoria) e o banco ( instancias persistidas).
    3.a) O repositorio deve funcionar da mesma forma para qualquer sistema de persistencia pois ele não dependente disso.

Exemplo de repositorio com persistencia

class RepositorioTurma {

     PersistenceAgent agent;
     RepositorioAluno repAlunos;

    public void add (Turma turma){

            agent.insertOrUpdate(turma);
            for (Aluno a : turma){
                 repAlunos.add(a);
            }

    }

}

Mais uma vez não chamei nada de DAO para que seja claro o papel de cada objeto.
Agora vc diz assim:

  1. “Mas porque colocar mais uma camada se posso chamar o agente de peristencia directamente ?”

O ponto é: Vc pode mas vc não quer. Se vc quer, vc não entender para que serve o repositorio.

  1. “Porque não implementar um repositorio generico?”

Vc pode implementar generico, mas o dominio deve ter acesso como se fosse individual
Mas é licito fazer algo como

E usar um mecanismo de injeção/fabrica. O ponto é que se eu pedir o repositorio de TipoDeProduto e eu não especificar qual implmentação quero, o metodo retorna uma implementação generica. Mas se eu definir um classe de implementação especifica, deve ser essa a retornada. O objetivo do repositorio é ter métodos especificos/especiais de busca que sejam directamente acoplados às regras de negocio.
Essa é uma grande diferença do DAO onde esses métodos devem ser fracamente acoplados às regras de negocio. Em DAO vc usa um criteria. No repositorio vc cria uma logica de negocio.

Um outro exemplo simples de como DAO e Repositorio são difernetes.
(sintaxe simplificada)

RepositorioAccountTransaction{

 public void addAccountTransaction(AccountTransaction t ){

    RecordMapper mapper = new RelectionMapper();
    mapper.exclude( new MoneyFieldFilter() ); // esclui campos to tipo Money
  
    Record t_rec = mapper.map(t);
    Money value = t.value;
    t_rec.setAmount(value.getAmount()); 
     
    persistenceAgent.insertOrUpdate(t_rec);

    RepositoryAccount.add(t.from);
    RepositoryAccount.add(t.to);

    
}

public List&lt;Transactions&gt; getTransactions(Date date){

     RecordQuery query = "from transaction where date = " + date; // simbolico

     List&lt;Record&gt; records =  persistenceAgent.execute(query); 

     List &lt;Transactions&gt; res = new ... 

     Currency c = Currency.newInstance("BRL"); // poderia ler de tabela
 
     for (Record r : records) {
             t =  mapper.unMap(r);
             t.value = new Money ( r.getAmount() , c);
             res.add(t);
     }
     
  return res;
}

}

}

Pode até haver um forma de setar Money com uma Currency usando Hibernate. Esse não é o ponto.
O ponto é que o repositorio sabe , onde e como encontrar as coisas e produzir objetos de dominio corretamente.
Ele sabe regras de dominio. DAOs não devem saver regras de dominio.

Não sei mais o que dizer…

Thiago_Senna

Fabio Kung:
Mas depende do caso. Naquela discussão de ter um repositório dentro de entidades ou não, se você escolhe por deixar repositórios dentro de entities, o repositorio acaba servindo para definir relacionamentos complicados como: “1 Turma possui N Alunos transferidos de outros estados”.

class Turma { private final RepositorioDeAlunos rep; ... public List&lt;Aluno&gt; getAlunosTransferidosDeOutrosEstados() { return this.rep.getAlunosTransferidos(&lt;de&gt;, &lt;para&gt;); } }

Só para ficar claro: Nesta consulta é retornado os alunos transferido de um estado X para um estado Y e que seja desta turma, correto?

Pois nesta idéia de juntar a collection com o repository passa a ficar confuso se temos um repositório para todos os alunos persistidos no sistema ou se é apenas um repositório de alunos para uma turma em específico.

Afinal, é mais coerente uma turma ter seu próprio repositório de alunos ou ter um repositório de alunos para o domínio?

sergiotaborda

Thiago Senna:

Afinal, é mais coerente uma turma ter seu próprio repositório de alunos ou ter um repositório de alunos para o domínio?

Repositorio é para todos os aluns.
Turma não precisa de um repositorio de alunos. Ela mesma contém os alunos! :shock:

class Turma {
 
    Collection alunos;

}
Thiago_Senna

sergiotaborda:
Thiago Senna:

Afinal, é mais coerente uma turma ter seu próprio repositório de alunos ou ter um repositório de alunos para o domínio?

Repositorio é para todos os aluns.
Turma não precisa de um repositorio de alunos. Ela mesma contém os alunos! :shock:

Sim, entendi! Mas o que quero agora é questionar isso. Se a turma já tem os alunos, para que também guardá-los em repositórios? Olhando por este ponto de vista não parece que estou tendo que trabalhar bem mais?

Thiago_Senna

Só pra somar: se eu quiser persistir em XML por exemplo, basta que a collection seja uma implementação de XmlArrayList do XStream (http://xstream.codehaus.org/persistence-tutorial.html). Precisaria de alguns cuidados, mas é um objetivo tranquilamente alcançavel. Eu mato a persistência e o relacionamento em uma paulada só.

[color=red]UPDATED:[/color]
Imagine que posso fazer uma ‘DI’ e dizer qual é a implementação da collection que a aplicação deve utilizar? Deve dar um bom trabalho, mas parece ser possível colocar uma implementação de Collection encarregada de se manter sincronizada com um hibernate da vida.

tnaires

Isso só acontece quando insistimos em colocar métodos de negócio dentro do DAO. Mas o DAO não foi feito pra isso.
AHM??? Pode explicar isso? Quem disse isso?

Então você concorda que devemos colocar regras de negócio no DAO, que foi feito apenas para cuidar de questões envolvendo persistência?

Claro que existem situações onde isso é adequado, mas conceitualmente é errado.

Conforme afirmou o sergiotaborda:

sergiotaborda

A ideia não é injectar coleções, é injetar mecanismos de persistencia.
Para que isto dê certo as suas classes não devem depender do mecanismo.
Um mecanismo realmente generico é hand-off. Ou seja, ele pega seus objetos e faz o trabalho
de persistir. O máximo que podemos dar a estes mecanismos são metadados.

Usar classes especiais de Collection não dão certo.

Eu entendo o que quer dizer com ter que adionar em turma e depois em repositorio. O vc não está entendendo é que essa adição é transiente. Ou seja, se vc adicionar aluno a turma, mas se arrepender e não remeter turma ao repositorio com o seu mecanismo de “auto-adição” o aluno já fica no repositorio de alunos mesmo quando eu anular o processo.

Turma t = new Turma(); // 0 turmas, 0 alunos
Aluno a = new Aluno();  // 0 turmas, 0 alunos
t.addAluno(a);//  0 turmas, 1 alunos

// abort

O repositorio de alunos ficou com um aluno porque eu o adiconei em turma. Isto é um erro.
Enfim , a sua ideia simplesmente não funciona. Vc tem que olhar “the big picture” porque ha muitas coisas envolvidas e não apenas add/remove.

tnaires

sergiotaborda:
Repositorio é para todos os aluns.
Turma não precisa de um repositorio de alunos. Ela mesma contém os alunos! :shock:

class Turma {
 
    Collection alunos;

}


Sérgio, acho que depende. Se os requisitos do sistema exigirem que eu armazene alunos que não estão em turma alguma, então precisarei de um repositório explícito de alunos correto?

sergiotaborda

tnaires:
sergiotaborda:
Repositorio é para todos os aluns.
Turma não precisa de um repositorio de alunos. Ela mesma contém os alunos! :shock:

Sérgio, acho que depende. Se os requisitos do sistema exigirem que eu armazene alunos que não estão em turma alguma, então precisarei de um repositório explícito de alunos correto?

Concerteza. Vc sempre terá um repositorio de alunos. O repositorio representa TODOS os alunos que existem no dominio. Alguns estarão agrupados em turmas outros não.
Agora, no grafo da turma, têm que estar os alunos. Claro que em termos de implementação vc pode simular que os alunos estão na turma sem usar uma coleção implicita. mas isso complica a longo praso. Quanto mais desacoplado for a entidade do repositorio melhor.

// opção de não usar collectino em turma

Turma {

   List&lt;Aluno&gt; getAlunos(){
          return AlunosRepository.getAlunos(this);
   }

   addAluno ( Aluno aluno) {
          AlunosRepository.addAlunoTurma(this, aluno);
   }
}
Fabio_Kung
sergiotaborda:
Cara, uma coisa é o conceito e a utilidade do repositorio. Outra coisa é ORM. Misturar os dois dá merda.

O problema é que @manyToOne resolve em certas circusntâncias. Como já foi dito : se usar hibernate é tudo uma maravilha, mas se usar JDBC é um pesadelo. Mas Hibernate e JDBC são tecnologias de persistencia.


Ahn?
Problema: quero os alunos dada uma turma.

ORM decente:
List&lt;Aluno&gt; alunos = turma.getAlunos();
JDBC Puro:
List&lt;Aluno&gt; alunos = repositorio.getAlunosDa(turma);

Sem ter um repositório dentro da entidade, não há como fazer a primeira alternativa usando jdbc puro. Portanto a escolha de usar um orm decente impacta sim em como você vai usar os seus repositórios. Não dá para nivelar sempre por baixo e fazer tudo como se você estivesse usando só JDBC puro. Para que ignorar o fato de que existe um ORM que permite níveis de abstração maiores?

sergiotaborda:
um repositorio (repositorio : onde vc coloca as coisas e depois pega de volta) não tem nada a ver com persistencia. (persistir: manter por muito tempo) Já existiram vários topicos sobre isto , e eu digo sempre a mesma coisa, então não vou me repetir. 1) Entender o papel do Repositorio. Entender que o repositorio serve escatamente para não existirem ActiveRecords. 2) Entender que Repositorio é diferente de DAO. Eu sei que é dificil , mas ao menos tentem. Repositorio : prover acesso centralizado ao conjunto de instancias de uma entidade. DAO: prover acesso padronizado a N diferentes tencologias de persitencia cujas API não são equivalentes.


Sérgio, eu acho muito válido você explicar quantas vezes você quiser o que é Repositório. Mas acho que para mim não precisa. O que eu disse não é um indicativo de que eu não sei o que são Repositórios e o que eles representam.

sergiotaborda:
A questão é : Entender que o repositorio é tão ou mais consciente das relações entre as entidades que a entidades elas mesmas.
sergiotaborda:
Turma não precisa de um repositorio de alunos. Ela mesma contém os alunos! :shock:
Você se contradiz aqui. O repositório que cuida das relações mas a Turma mantém a relação?
sergiotaborda:
3.a) O repositorio deve funcionar da mesma forma para qualquer sistema de persistencia pois ele não dependente disso.
Isso é nivelar por baixo. A escolha do framework orm impacta sim onde você vai ou não precisar de um repositório.
sergiotaborda:
O ponto é que o repositorio sabe , onde e como encontrar as coisas e produzir objetos de dominio corretamente. Ele sabe regras de dominio. DAOs não devem saver regras de dominio.
Eu só não entendi o que todo o seu discurso sobre Repository vs Dao tem a ver com o meu comentário, de que você não precisa fazer com que todas as relações entre os objetos devam ser gerenciadas pelo repositório. A engine de orm resolve isso para mim em vários casos, a um custo bem menor.

Claro que @ManyToOne não resolve tudo. Aí você tem várias alternativas, entre elas:

1) Criar um novo relacionamento para implementar o seu requisito:
class Turma {
  @OneToMany
  private List&lt;Aluno&gt; alunos;

  @OneToMany
  private List&lt;Aluno&gt; alunosComNotaMaiorQueDez;
}
2) Repositorio dentro da turma e fazer:
turma.getAlunosComNotaMaiorQue(10);
3) ou finalmente:
repositorio.getAlunosComNotaMaiorQuePara(10, turma);
Fabio_Kung
sergiotaborda:
Quanto mais desacoplado for a entidade do repositorio melhor.
// opção de não usar collectino em turma

Turma {

   List&lt;Aluno&gt; getAlunos(){
          return AlunosRepository.getAlunos(this);
   }

   addAluno ( Aluno aluno) {
          AlunosRepository.addAlunoTurma(this, aluno);
   }
}
Você não percebe que esse seu repositório não tem NADA de diferente de uma Collection?
class Turma {
  List&lt;Aluno&gt; alunos;

   List&lt;Aluno&gt; getAlunos(){
          return this.alunos;
   }

   addAluno(Aluno aluno) {
          this.alinos.add(aluno);
   }
}
Collections são repositorios!
sergiotaborda:
(repositorio : onde vc coloca as coisas e depois pega de volta)
Fabio_Kung
Thiago Senna:
Só pra somar: se eu quiser persistir em XML por exemplo, basta que a collection seja uma implementação de XmlArrayList do XStream (http://xstream.codehaus.org/persistence-tutorial.html). Precisaria de alguns cuidados, mas é um objetivo tranquilamente alcançavel. Eu mato a persistência e o relacionamento em uma paulada só.

[color=red]UPDATED:[/color]
Imagine que posso fazer uma 'DI' e dizer qual é a implementação da collection que a aplicação deve utilizar? Deve dar um bom trabalho, mas parece ser possível colocar uma implementação de Collection encarregada de se manter sincronizada com um hibernate da vida.

Isso é verdade Thiago. Mas fazer seu repositorio implementar Collection não faz com que você possa injetar uma implementação de Collection do XStream, justamente pq vc não usa o Repositorio como Collection. Você precisa usar os metodos especificos:
class Turma {
  // nao estou injetando so para deixar claro que vc quer usar o repositorio como lista...
  private List&lt;Aluno&gt; alunos = new AlunoRepository();

  public getAlunosComNotaMaiorQue(int nota) {
    // nao consigo usar os metodos especificos do repositorio...
  }
}
No fim das contas, o que vc vai acabar usando na sua Entidade é:
class Turma {
  // nao estou injetando so para deixar claro que vc quer usar o repositorio como lista...
  private AlunoRepository alunos = new AlunoRepository();

  public getAlunosComNotaMaiorQue(int nota) {
    return alunos.blablabla(nota, this);
  }
}
E aí não adianta você querer injetar nessa entidade uma implementação de List qualquer. A XmlList do XStream (viu o @author dela? :D ) não tem o metodo blablabla().

Pegou?

Laercio_Queiroz

Quando vc recupera os dados para reconstruir a turma, não recupera tbm a coleção de alunos que é atributo da turma? (Excetuando os casos de lazy loading)

Concordo e o próprio Evans escreveu sobre isso:

Thiago_Senna
sergiotaborda:
Vc tem que olhar "the big picture" porque ha muitas coisas envolvidas e não apenas add/remove.

Mas... o add/remove é mesmo o menor dos problemas. Mas inicialmente, se eu levasse essa idéia pra frente tentando implementar algo, imagino que não preciso nem ao menos sobrescrever add/remove. Eles já estariam prontos, do jeito que estão hoje. Eu só quero acrescentar nestas collections buscas mais específicas. A implementação destas buscas mais específicas seria delegado para o mecanismo de persistência.

Se o assunto é JDBC, o bicho pega. Mas se fosse possível:
turma.getAlunos().getAdicionados();
turma.getAlunos().getAlterados();
turma.getAlunos().getRemovidos();
Aí, lá no DAO você poderia usar estes métodos para sincronizar com o BD. Mas isso é só uma idéia. Não leve este exemplo a sério, hehe! Mas isso é em IMHO um exemplo de que as collections poderiam ser mais espertas.

Se o assunto é hibernate o negócio é mais fácil. Até a injeção de dependência talvez fique mais fácil, pois nem vai precisar (em parte). Os dados que são inseridos e removidos da coleção o próprio hibernate gerencia à partir dos mapeamentos. Só preciso mesmo do gran finale de como implementar a coleção sem ter muito trabalho (sou muito preguiçoso, hehe).

imagine:
turmas = session.find("from Turma"); // coloquei o session hibernate aqui de propósito

for (Turma t: turmas) {
    List alunos = t.getAlunosReprovadosNoAnoAnterior(); 
    // o hibernate  garante que alunos na verdade seja uma coleção de AlunoPersistentCollection, por exemplo
}

O difícil mesmo é como fazer isso tudo! Mas se houver uma solução ténica viável e elegante, ai como fica o Repository? Ainda justificaria? Talvez aí comece a ser uma questão de gosto :)

Achei três artigos que não li ainda com o devido cuidado, mas parece ser um assunto similar ao que está sendo discutido neste tópico.

Custom Collections with NHibernate, Part I: Refactored - Billy McCafferty
Custom Collections with NHibernate, Part II: Refactored - Billy McCafferty
Custom Collections with NHibernate, Part III: Refactored - Billy McCafferty

Thiago_Senna
Fabio Kung:
No fim das contas, o que vc vai acabar usando na sua Entidade é:
class Turma {
  // nao estou injetando so para deixar claro que vc quer usar o repositorio como lista...
  private AlunoRepository alunos = new AlunoRepository();

  public getAlunosComNotaMaiorQue(int nota) {
    return alunos.blablabla(nota, this);
  }
}
E aí não adianta você querer injetar nessa entidade uma implementação de List qualquer. A XmlList do XStream (viu o @author dela? :D ) não tem o metodo blablabla().

Pegou?

Mais ou menos, Fabio. E se AlunoRepository é uma interface e:
private AlunoRepository alunos = new AlunoRepositoryXStreamImpl();
Poderia encapsular com composição por exemplo o XmlList, não posso? E se
private AlunoRepository alunos = new RepositoryFactory.getAlunoRepositoryHibernateImpl(this);
Então esse código:
public getAlunosComNotaMaiorQue(int nota) {
    return alunos.blablabla(nota, this);
  }
poderia ficar assim:
public getAlunosComNotaMaiorQue(int nota) {
    return alunos.blablabla(nota);
  }
Tirei o this, pois se estou juntando collection + repository é melhor que a própria collection saiba qual é a turma que ele está relacionado. Por isso usei: "new RepositoryFactory.getAlunoRepositoryHibernateImpl(this)"

Sobre o autor do XmlList, eu já vi quem é o autor sim. Aliás, todo aquele pacote 'persistence' é tudo do mesmo autor. Será que posso confiar(risos)?

Caso eu tenha escrevido todo este post e não tenha conseguido contornar o problema que você viu, é por que eu não peguei o problema :( Caso seja este o caso, explique denovo, por favor!

sergiotaborda

Fabio Kung:
sergiotaborda:
Cara, uma coisa é o conceito e a utilidade do repositorio. Outra coisa é ORM.
Misturar os dois dá merda.

O problema é que @manyToOne resolve em certas circusntâncias. Como já foi dito : se usar hibernate é tudo uma maravilha, mas se usar JDBC é um pesadelo. Mas Hibernate e JDBC são tecnologias de persistencia.


Ahn?
Problema: quero os alunos dada uma turma.

ORM decente: List&lt;Aluno&gt; alunos = turma.getAlunos();
JDBC Puro: List&lt;Aluno&gt; alunos = repositorio.getAlunosDa(turma);

Sem ter um repositório dentro da entidade, não há como fazer a primeira alternativa usando jdbc puro. Portanto a escolha de usar um orm decente impacta sim em como você vai usar os seus repositórios. Não dá para nivelar sempre por baixo e fazer tudo como se você estivesse usando só JDBC puro. Para que ignorar o fato de que existe um ORM que permite níveis de abstração maiores?

E depois vc se interroga porque fico explicando a diferença entre repositorio e DAO.
Um mecanismo ORM é um tipo especifico de DAO. Então não interessa que mecanismo eu estou usando para a persistencia porque o DAO me isola disso. Esse é o papel do DAO: isolar a API de persistencia. Seja JDBC, seja Hibernate seja o que for.
Eu posso fazer List<Aluno> alunos = turma.getAlunos() em qualquer esquema de persistencia. Eu não apenas posso, como DEVO. Porquê? Pela simples razão que esse código representa uma relação direta entre turma e aluno. É uma relação de dominio. É uam relação do modelo! Não devo simplesmente usar o segundo codigo. Pelo menos não se estiver usando DDD.

(Tlv seja uma falha minha, mas se a pessoa fala em repositorio eu assumo que ela está usando DDD e portanto o objetivo final é ter classes de dominio corretas. Agora, se vc está falando fora de DDD ai vc faz o que quiser e como quiser. Só que se vc falar de repositorio fora de DDD não faz sentido absolutamente nenhum. Já que se não um modelo de dominio bem definido, vale tudo.)

Depois vc está esquecendo que as classes do dominio/modelo são desacopladas da intraestrutura. Logo, aquele codigo vai funcionar em qualquer ocasião porque o Repositorio vai se encarregar que assim seja. Que truques o repositorio usa para fazer isso não interessa. 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.

de um lado da moeda:
O objeto turma mantém as suas relações de MODELO. A turma tem alunos e está assignada a professores.
Tem um horario, etc… No modelo, Turma tem tantas relaçoes como a turma real tem no mundo real.
O objeto turma é consciente que tem uma coleção de alunos associada a ele e ele permite acesso a ela com getAlunos(). Isto é modelagem. Isto é orientação a dominio.

do outro lado da moeda:
O repositorio da turma tem que garantir que o objeto turma seja incluido na lista de turmas existentes e que o seu estado seja conservado. Porque quando o dominio puxar um “getTodasTurmas()” essa turma tem que estar lá. E tem que estar lá com todo o seu grafo de objetos relacionados. Alunos, Professores, etc…
O ponto é que o repositorio da turma sabe conservar turmas. Apenas turmas. Portanto ele tem que delegar a outros repositorios a conservação dos outros tipos. O ponto é que ele sabe como uma turma é formada. O repositorio é um objeto do dominio. Ele tem conhecimento das entidades e das suas relações e ele vigia que essas relações seja mantidas de forma coerente. Então, o repositorio sabe que turma tem vários alunos e professores e ele sabe que tem que delegar a conservação desses tipos aos repositorios apropriados.
Isto ainda é orientação ao dominio, mas agora não do ponto de vista da turma individual, mas do ponto de vista do cara que tem que manter todas as turmas. O objeto turma está preocupado em manter um coleção dos seus alunos. Os repositorios estão preocupados em manter a coleção e todas as turmas e todos os alunos.

O primeiro caso vc resolve facil com uma coleção privada e desacoplada : um arraylist da vida.
O segundo caso vc não resolver apenas um coleções porque vc precisa de alguma logica que destinga de entre todos os alunos do sistema, quais pertencem a qual turma.

Na “cabeça” de um repositorio não existem bancos de dados. Existem coleções de objetos e relações entre eles que têm que ser mantidas. Mas elas são mantidas com logica programada no repositorio e não com mecanismos externos.

Por isso que ue falei que é necessário entender a diferença entre repositorio e DAO e onde fica a fronteira.
As informações de um repositorio não têm obrigação de ser persistentes. A persistencia é um mecanismo artificial que permite que o programa deixe de executar e depois volte a executar como o estado inalterado. Só isso. Não é a razão de vida de um sistema. Muito menos de um repositorio.

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;


}

}

Não. A escolha de ter um repositorio se deve a vc querer um modelo simples, rico e independente de estrutura.
Vc tem um repositorio se vc está querendo esses objetivos.
Se além disso vc quer persistencia, vc tem um DAO ( que pode ser JDBC puro sim)
Se além disso vc quer ter um mecanismo padrão para o DAO com JDBC vc usa ORM.
Entenda que ORM não é o objetivo é apenas uma ferramenta.
se o o seu DAO é JCache ou JGroups não ha ORM. Se o seu dao é XML não ha ORM.

Se a escolha do ORM inpacta o seu modelo então para que serve o DAO ? Afinal que isolamento é esse ? Isolamento poroso ? Se o ORM impacta o modelo o seu sistema é fortemente acoplado. Pior ainda, só funciona dessa maneira. Então para quê complicar com DAO e repositorios ? Arranque isso fora , deixe o ORM puro e pronto. Vai ganhar um monte em simplicidade e poupar um monte de camadas inuteis no seu sistema.

Os padrões têm objetivos. Se vc não tem esses objetivos porque usar os padrões ? Moda ?

Eu já falei isto tantas vezes mas parece que não me consigo fazer entender. :cry:

Fabio_Kung

Quando vc recupera os dados para reconstruir a turma, não recupera tbm a coleção de alunos que é atributo da turma? (Excetuando os casos de lazy loading)

Pode sim. Mas aí você vai acabar fazendo o seu próprio ORM, concorda? Vai ter que cair nessas questões de implementar ou não lazy-loading já que é proibitivo trazer todos os relacionamentos sempre. Aí acaba caindo no caso de usar um orm (mesmo que seja você mesmo que fez).

Laércio Queiroz:
Concordo e o próprio Evans escreveu sobre isso:

Evans:

“Before implementing something like a REPOSITORY , you need to think carefully about the infrastructure you are committed to, especially any architectural frameworks. You may find that the framework provides services you can use to easily create a REPOSITORY , or you may find that the framework fights you all the way. You may discover that the architectural framework has already defined an equivalent pattern of getting persistent objects. Or you may discover that it has defined a pattern that is not like a REPOSITORY at all.”

“In general, don’t fight your frameworks. Seek ways to keep the fundamentals of domain-driven design and let go of the specifics when the framework is antagonistic. Look for affinities between the concepts of domain-driven design and the concepts in the framework. This is assuming that you have no choice but to use the framework. Many J2EE projects don’t use entity beans at all. If you have the freedom, choose frameworks, or parts of frameworks, that are harmonious with the style of design you want to use.”


Nem lembrava que ele tinha escrito isso. Obrigado!

Fabio_Kung

Thiago Senna:
Mais ou menos, Fabio. E se AlunoRepository é uma interface e:

Poderia encapsular com composição por exemplo o XmlList, não posso?


Não só pode como deve. O exemplo de repositório que eu estava dando fazia justamente isso, mas a composição era com org.hibernate.Session ao invés de XmlList. Duas implementações do repositorio (mesma interface).

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.

Laercio_Queiroz

Concordo com você Fábio, só quis dizer que apesar de ser chato (e desnecessário em vários casos) … é possível fazer isto com JDBC puro. :wink:

Edufa

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

Abstrai a lógica da persistencia

interface Repository<T> {
	PersistenceStrategy<T> getStrategy();
	void setPersistenceStrategy(PersistenceStrategy<T> strategy);
}  

interface PersistenceStrategy<T> {
	void store(T entity);
	Collection<T> list();
	Collection<T> list(Specification<T> specification);
}

Apenas para exemplificar, persistencia baseada em collection

class MemoryPersistenceStrategy<T> implements PersistenceStrategy {
	Collection<T> c = new ArrayList<T>();
	
	@Override
	public Collection<T> list() {
		return c;
	}

	@Override
	public Collection<T> list(Specification specification) {
		ArrayList<T> result = new ArrayList<T>();
		for (T entity : list()) {
			if (specification.isSatisfiedBy(entity))
				result.add(entity);
		}
		return result;
	}

	@Override
	public void store(Object entity) {
		c.add((T) entity);
	}
}

Usa objetos Specification para interagir com o Repository

interface Specification<T> {
  boolean isSatisfiedBy(T entity);
}

Exemplos de Specification

class NotaAbaixoDaMediaSpecification implements Specification<Aluno> {
	private int notaDeCorte;
	
	public NotaAbaixoDaMediaSpecification(int notaDeCorte) {
		this.notaDeCorte = notaDeCorte;
	}

	@Override
	public boolean isSatisfiedBy(Aluno entity) {
		return (entity.getNota() < notaDeCorte);
	}
}

class AlunosDaTurmaSpecification implements Specification<Aluno> {
	private Turma turma;
	
	public AlunosDaTurmaSpecification(Turma turma) {
		this.turma = turma;
	}

	@Override
	public boolean isSatisfiedBy(Aluno entity) {
		return entity.getTurma() == turma;
	}
}

Um exemplo de repository

class AlunoRepository implements Repository<Aluno> {
	private PersistenceStrategy<Aluno> persistenceStrategy;
	
	@Override
	public PersistenceStrategy<Aluno> getStrategy() {
		return persistenceStrategy;
	}

	@Override
	public void setPersistenceStrategy(PersistenceStrategy<Aluno> strategy) {
		this.persistenceStrategy = strategy;
	}
	
	public Collection<Aluno> list() {
		return persistenceStrategy.list();
	}

	public Collection<Aluno> list(Specification<Aluno> specification) {
		return persistenceStrategy.list(specification);
	}

	public void store(Aluno entity) {
		persistenceStrategy.store(entity);
	}
}

O aluno, bem simples

class Aluno {
	private String nome;
	private int nota;
	private Turma turma;
	
	public Aluno(String nome, int nota) {
		this.nome = nome;
		this.nota = nota;
	}
	
	public String getNome() {
		return nome;
	}
	public int getNota() {
		return nota;
	}
	public Turma getTurma() {
		return turma;
	}
	void setTurma(Turma turma) {
		this.turma = turma;
	}
	public String toString() {
		return getNome()+" "+getNota();
	}
}

E a turma, tb bem básico, no caso eu passei o repositorio de alunos via construtor.
Aqui vejo um possível problema, turma tem acesso a todos os lunos através do repositorio, uma turma tem de gerenciar os seus alunos e seu conhecimento deveria se restringir a eles.

class Turma {
	AlunoRepository alunoRepository;
	
	public Turma(AlunoRepository alunos) {
		alunoRepository = alunos;
	}
	
	public void increverAlunoNaTurma(Aluno aluno) {
		aluno.setTurma(this);
		alunoRepository.store(aluno);
	}
	
	public Collection<Aluno> getAlunos() {
		return alunoRepository.list(new AlunosDaTurmaSpecification(this)); 
	}
}

Um pqno teste para ver funcionando

public class Teste {
	
	public static void main (String args[] ) {
		AlunoRepository alunos = new AlunoRepository();
		alunos.setPersistenceStrategy(new MemoryPersistenceStrategy<Aluno>());
		
		Turma turmaA = new Turma(alunos);
		turmaA.increverAlunoNaTurma(new Aluno("José", 6));
		turmaA.increverAlunoNaTurma(new Aluno("João", 7));
		turmaA.increverAlunoNaTurma(new Aluno("Maria", 8));
		Turma turmaB = new Turma(alunos);
		turmaB.increverAlunoNaTurma(new Aluno("Ana", 7));
		
		Specification<Aluno> notaAbaixo = new NotaAbaixoDaMediaSpecification(8); 
		System.out.println("=== Alunos da Turma A");
		for (Aluno a : turmaA.getAlunos()) {
			System.out.println(a);
			if (notaAbaixo.isSatisfiedBy(a))
				System.out.println(" Abaixo da Média");
		}
		System.out.println("=== Alunos da Turma B");
		for (Aluno a : turmaB.getAlunos()) {
			System.out.println(a);
			if (notaAbaixo.isSatisfiedBy(a))
				System.out.println(" Abaixo da Média");
		}
		System.out.println("Fim");
	}
}

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?

sergiotaborda
Fabio Kung:
sergiotaborda:
Quanto mais desacoplado for a entidade do repositorio melhor.
// opção de não usar collectino em turma

Turma {

   List&lt;Aluno&gt; getAlunos(){
          return AlunosRepository.getAlunos(this);
   }

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

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.

sergiotaborda

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?

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.

Edufa

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.

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.

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?

Thiago_Senna

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.

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.

sergiotaborda

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!

Fabio_Kung

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.

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?

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


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.

fabim

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

Fabio_Kung

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

Fabio_Kung

Daos servem pra dividir responsabilidade de q? Mas DAO’s nao são apenas Data-Mappers? Que deveriam ser plugáveis?
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.

Thiago_Senna
sergiotaborda:
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.

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

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.

sergiotaborda:
O codigo de repositorio de turmas é mais ou menos assim:
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);
          }
  }

}

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?

sergiotaborda:
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.
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.

fabim

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

sergiotaborda

Fabio Kung:
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.

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?

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.

Edufa

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.

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

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.

Fabio_Kung

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

sergiotaborda

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.

Fabio_Kung

sergiotaborda:
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.

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.


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

sergiotaborda

fabiocsi:
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.

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

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.

Thiago_Senna

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

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:

Criado 6 de março de 2008
Ultima resposta 7 de mar. de 2008
Respostas 57
Participantes 9