Repositórios trabalham com entidades, mas e se

11 respostas
hlegius

… eu não poder/necessitar criar uma entidade para devolver ao serviço ?

Por exemplo, para retornar um produto pelo “id”, ele retornará ao serviço a entidade Produto. Mas e se eu apenas precisar retornar quantos itens foram encontrados (sim, eu uso DAO’s e sem ORM). A grosso modo, na DAO teriamos algo como:

Nesse caso, eu apenas receberia um inteiro contendo o total encontrado. Tendo isto em mente, teria sentido passar essa informação ao repositório ?
Na minha opinião, até faz sentido, porque a DAO é um mundo diferente do mundo do Domínio e suas entidades, mas por outro lado, seria apenas um “passa-repassa” entre DAO-Repositório-Serviço até chegar na Controladora/whatever-you-want o valor “10” por exemplo.

O que os senhores acham a respeito ?

Obs: poderia usar um SELECT * e retornar uma coleção de Produto, mas acho que isto iria gastar processamento desnecessário e ao meu ver, ficaria mais ‘feio’

Abraços!

11 Respostas

GraveDigger

Se é uma informação relevante para o negócio, deve estar no Repositório sim.

O ponto não é a complexidade da tarefa, mas sim seu objetivo.

[]'s

sergiotaborda
hlegius:
... eu não poder/necessitar criar uma entidade para devolver ao serviço ?

Por exemplo, para retornar um produto pelo "id", ele retornará ao serviço a entidade Produto. Mas e se eu apenas precisar retornar quantos itens foram encontrados (sim, eu uso DAO's e sem ORM). A grosso modo, na DAO teriamos algo como:

SELECT COUNT(1) FROM produtos...

Nesse caso, eu apenas receberia um inteiro contendo o total encontrado. Tendo isto em mente, teria sentido passar essa informação ao repositório ?
Na minha opinião, até faz sentido, porque a DAO é um mundo diferente do mundo do Domínio e suas entidades, mas por outro lado, seria apenas um "passa-repassa" entre DAO-Repositório-Serviço até chegar na Controladora/whatever-you-want o valor "10" por exemplo.

O que os senhores acham a respeito ?

A solução é simples embora, tlv, um pouco contra intuitiva.

O repositorio não retorna os objetos de dominio. Ele retorna o resultado da pesquisa. Esse resultado permite encontrar outras informações da pesquisa além dos dados em si.
É semelhante ao objeto ResultSet do JDBC. Vc não obtém objetos "linha" no jdbc.

Então , vc cria um interface Query com os seguintes métodos

interface Query<T> {

        public T find();
        public Collection<T> list();
        public int count();
        public boolean isEmpty();

}

Ai vc implementa como quiser. A implementação padrão é: list retorna a coleção com o resutado e contendo todos os objetos encontrados. find retorna só o primeiro ou null se a pesquisa é vazia. count retorna list().size()
e isEmpty retorna list().isEmpty();

Agora, para um repositorio que use JDBC vc pode implementar as coisas de forma diferente. count , por exemplo, executaria um select diferente que apenas retorna a contagem de itens sem ter que ler os itens ( é isso que vc quer). isEmpty pode executar o mesmo select e comparar se é zero. find pode limitar a procurar ao primeiro item como um Top 1 (SQL SErver ) ou limit (postgres e outros...)

Enfim, com esta interface vc pode escolher como executar o sql porque sabe qual seria mais eficiente.

Outra vantagem do Query é que para repetir a query vc não precisa do repositorio. vc simplesmente invoca o método do query de novo. Se existirem dados novos, eles viram no resultado. Isto é prático especialmente se vc adicionar mais um método list(start, count) que retorna os count seguintes itens a partir de start. Isto é util para paginação.

hlegius

Se ele retorna os resultados da pesquisa, é correto retornar por ele um “inteiro” contendo o valor da pesquisa do count() correto ?

Entendi. Estive reparando que o Repository serve para buscar dados, trazer list/Collections … não é necessariamente tarefa dele as outras coisas, como adicionar, atualizar, remover ou até mesmo, trazer um valor inteiro - o count().
Então, essa Query object se parece meio com o Repository no aspécto do list()… ou seja, fiquei meio perdido com isto. Um se comunica com o outro, ou cada um faz um papel diferente e nem se encontram no sistema ?

Procurei um pouco sobre QueryObject, mas não descobri se ele é do lado da infra ou se é pertencente ao domínio… acho que meu cerebro deu um nó =)
Ao que reparei, o pessoal usa junto a Criteria que tudo isso junto acaba sendo o embrião de um ORM tipo hibernate…

Se eu falei só besteira, realmente é porque o nó está bem atado =(

sergiotaborda
hlegius:
Agora, para um repositorio que use JDBC vc pode implementar as coisas de forma diferente. count , por exemplo, executaria um select diferente que apenas retorna a contagem de itens sem ter que ler os itens ( é isso que vc quer). isEmpty pode executar o mesmo select e comparar se é zero. find pode limitar a procurar ao primeiro item como um Top 1 (SQL SErver ) ou limit (postgres e outros...)

Entendi. Estive reparando que o Repository serve para buscar dados, trazer list/Collections ... não é necessariamente tarefa dele as outras coisas, como adicionar, atualizar, remover ou até mesmo, trazer um valor inteiro - o count().
Então, essa Query object se parece meio com o Repository no aspécto do list()... ou seja, fiquei meio perdido com isto. Um se comunica com o outro, ou cada um faz um papel diferente e nem se encontram no sistema ?

Procurei um pouco sobre QueryObject, mas não descobri se ele é do lado da infra ou se é pertencente ao domínio... acho que meu cerebro deu um nó =)
Ao que reparei, o pessoal usa junto a Criteria que tudo isso junto acaba sendo o embrião de um ORM tipo hibernate...

Ok, os nomes não ajudaram. QueryObject é um padrão para vc passar os parâmetros para o Repositorio. É o input da pesquisa.
O objeto que me referi é o output da pesquisa o resultado. Não sei se existe um padrão para isto, mas se existisse seria algo como ResultObject.

Um repositorio ideal ( no sentido de ser proximo da ideia ) teria vários métodos de pesquisa, cada um recebendo um objeto com os parametros da pesquisa e retornando um objeto de resultado. (Como exemplo, pense no statement do SQL. Ele recebe um String como parametro e retorna um ResultSet como resposta).

public void ResultObject find(QueryObject  queryParams)

O respositorio realmente devolve uma lista com os objetos que encontrou, mas isso não significa que os métodos retornem List.
Quando vc usa List , List é o seu ResultObject. Repare que vc pode usar a interface de List para implementar as mesma coisa que eu falei que poderia com Query. Afinal List tem métodos para saber o tamanho (size) , se é vazio ( isEmpty) etc... Mas List tem um conjunto de outros métodos que não são relevantes como retainAll e removeAll. Enfim, vc pode implementar uma classe que implementa List e utiliza SQL diferente para implementar cada parte. Mas vc tem pouca flexibilidade.

Do ponto de vista do design é mais correto ter um objeto especifico para ser o seu ResultObject. Este objeto terá uma interface mais simples que List e portanto vai libertá-lo de ter de implementar métodos exotiericos como retainAll().

Mesmo assim, esse objeto terá que permitir acesso aos objetos que são o resultado da pesquisa. Por isso ele tem um método que retorna um collection. Mas o programador pode invocar outros métodos mais uteis e mais eficiente como count();

Ou seja, invocar resultObject.count() é mais rápido que invocar resultObject.list().size() porque por detrás dos panos vc não está carregando nenhum objeto realmente.

Aqui chegamos na questão da implementação. Como seu design segue SoC e o objeto de retorno é uma interface, vc pode simplesmente implementá-lo de várias formas conforme for mais util. O seu conhecimento do backend pode ser utilizado para aumentar a eficiencia e portanto a velocidade e diminuir o consumo de memória.

Vc pode usar vários padrões agora porque o objeto que vc retorna apenas tem que implementar uma interface simples.
(Flyheight e Fastlane são exemplos)

Se vc apenas carrega um List e devolve vc ainda posa fazer isso com o obecjto de retorno.
Por exemplo ( e chamando de QueryResult o objeto que chamei antes de Query para não confundir)

public class Repositorio {

 public QueryResult findActive(){
          List todosActivos = // .. executa o query que retorna um list com o resultado

          return new ListQueryResult(todosActivos);

 }
}


public class ListQueryResult<T> implements QueryResult<T>{

  List<T> all;

   ListQueryResult  (List<T> all){
       this.all = all;
   }
  
   public  T find (){
         return all.isempty ? null : all.get(0);
   }

   public Collection<T> list(){
        return Collections.unmodifiableCollection(all);
  }

  public int count (){
       return all.size();
  }

  public boolean isEmpty (){
       return all.isEmpty();
  }
   
}

Mas vc poderia implementar assim :

public class Repositorio {

 public QueryResult findActive(){
          
        // não executa nada
          return new SQLQueryResult (datasource);

 }
}


public class SQLQueryResult <T> implements QueryResult<T>{

   DataSource datasource;

   ListQueryResult  (DataSource datasource{
       this.datasource = datasource;
   }
  
   public  T find (){

         ResultSet rs = execute("SELECT TOP 1  * FROM table ... ");

           if (rs.next){
               return readObject(rs);
          } else {
               return null;
          }

   }

   public Collection<T> list(){

         ResultSet rs = execute("SELECT * FROM table ... ");

         List all = new ArrayList();

          while (rs.next){
               all.add(readObject(rs));
          } 
        return Collections.unmodifiableCollection(all);
  }

  public int count (){
         ResultSet rs = execute("SELECT count(1) FROM table ... ");

         rs.next
              
      return rs.getInt(1);
          
  }

  public boolean isEmpty (){
       return this.count() == 0;
  }
   
}

O codigo de invocação do SQL é apenas um exemplo. Vc teria que tratar exceptions, fechar connections , etc..
O DataSource é para obter a coneção (datasource.getConnection();)

A ideia é que cada método faz o seu proprio SQL ligeiramente diferente e otimizado para aumentar a eficiencia.

hlegius

Neste caso, o Repositório “ideal” teria várias pesquisas especializadas. Cada tipo de pesquisa receberia um QueryObject como parametro para podermos filtrar a informação a ser buscada igual você exemplificou, correto ?
Ou seja:

Ah sim, perfeito. Entendi seu ponto de vista. Implementar algo mais “direto” à implementar algo que me obriga a implementar comportamentos que eu não vou ultilizar.

Aqui é o ponto da dúvida. Meu Repository pode muito bem retornar ao meu Serviço um valor “não-collection/list”. Posso muito bem no meu serviço solicitar ao meu repositório que retorne-me o total encontrado, como por exemplo:

public void verificaTotal() { int totalFound = this.repositorio.count(queryObject); }

Isso seria equivalente a eu chamar o find().size() só que economizando uma boa memória, pois por detrás teriamos apenas um SELECT COUNT(1)/TOP 1…

Legal, acho que isso desata o nó que esse tópico tinha dado em minha cabeça ! Fico muito grato pela explicação completa que deu.

Agora, aqui você comentou sobre a QueryObject. Eu durante esse final de semana consultei alguns livros e li algumas coisas na rede sobre. Ao que pude entender, o QueryObject é o cara que “tira uma DAO” de jogo. Ele quem roda os SQL’s e devolve os resultados ao Repositório. Ele trabalha junto ao Criteria que é o cara responsável por criar os critérios de busca.

No exemplo que vi no livro, o Repositório do cara recebia uma Criteria como parametro e o Repositório implementava o QueryObject passando a entidade e a criteria por meio de setter injection.
A QueryObject dele montava a sentença SQL, buscava os dados e retornava o ResultSet ao repositório que montava a collection e devolvia assim ao serviço.

O Repositório dele se comporta de maneira bem similar ao que você explicou aqui na thread. Um list simples e “descompromissado” com métodos que não têm utilidade e o count() dele retorna o integer usando o TOP do SQL.
Tal implementação estaria correta ?

Agradeço a força !

Abraço!

sergiotaborda

Neste caso, o Repositório “ideal” teria várias pesquisas especializadas. Cada tipo de pesquisa receberia um QueryObject como parametro para podermos filtrar a informação a ser buscada igual você exemplificou, correto ?
Ou seja:

Ah! agora entendi o seu ponto. Vc acha que isso são pesquisas especializadas.
Não. São 3 métodos fazendo a mesma pesquisa e retornando informações diferentes dela.

Em um projeto onde vc tivesse a pesquisa “usuáriosAtivos” vc teria

findUsuariosAtivos : List
getFirstusuarioAtivo : usuario
countUsuariosAtivos : int

Para cada pesquisa vc teria 3 metodos. Para N pesqusias vc teria 3N métodos.
Com o outro modelo vc agrupa os métodos em um objeto de retorno. Então vc tem N pesquisas e N métodos.
muito mais simples , não ?

Repare que a frase SQL é a mesma ( porque a pesquisa é a mesma) apenas tem alguns parametros diferentes.
Se vc tiver 3 metodos vc vai escrever a pesquisa 3 vezes. quando a pesquisa mudar é provável que vc esqueça de mudar nos outros 2 métodos. Esse é o real problema da sua opção : manutenabilidade.

For que, vc fizer do seu jeito o seu repositorio vira um DAO. Acho que não isso que vc quer.

Não. Nada disso.
O QueryObject não faz nada. Ele é apenas um conjunto de dados. Um criteria do hibernate é um QueryObject.
O String SQL passado no statement é um QueryObject.

Em um repositorio os parametros são normalmente simples findByID(Integer id) , findByName(String name) , findActive() , findSelledIn (Date start, Date end), etc… quando vc tem muitos parametros vc vai querer combiná-lo num so objeto por exemplo
findSelledIn(Interval intervalo) seria equiavalente ao anterior mas com o start e o end dentro do obejto intervalo, que aqui é o seu QueryObject.

Agora, o QueryObject tende a ser mais complexo. A ideia é vc poder pedir aquelas 4 informações de forma combinada algo assim

QueryObject q = QueryObject.for(Product.class).
where("name").eq(name).
and("active").isTrue().
and("selled").in(intervalo)

Ai vc passa para o método find() e pronto. Bácisamente o seu repositorio começa a ter um só método. Só que isso destroi o repositorio. O objectivo do repositorio é manter e conter a contrução do QueryObject se vc já o opassa construido quer dizer que outro objeto o contruiu - que não o repositorio - e portanto está violando o papel do repositorio. O repositorio torna-se um DAO.

O DAO e o DomainStore funcionam bem com QueryObject. Repare como o QueryObject e SQL são bem semelhantes.
O repositorio recebe parametros mais simples, cria o QueryObject mais complexo que o DAO ou o DomainStore entende e o passa a ele. Depois recebe o resultado e trata esse resultado para ser retornado na estrutura certa.

aposto que isso não era um repositorio e sim um DAO. Pela simples razão que os repositorios não recebem QueryObject complexos ( como já expliquei ) e Criteria é um objeto complexo.
Isso que vc descreveu é o que o um DAO ou um DomainStore faria.

Para um DAO sim. Para um Repositorio não.
A Criteria que foi enviado ao objeto veio de onde ? Foi montada por quem ? Quem escolheu os parametros para ela ?
Em um sistema como Repositorio , seria o repositorios a fazer isso e a invocar esse objeto que vc descreveu.
em um sistema sem Repositorio , básicamente qualquer classe pode invocar o DAO, logo a construção do Criteria está espalhada pelo sistema inteiro. E isso é um problema. Esse é o problema que o repositorio tenta resolver. ele é um cara “acima” do DAO que sabe como criar Criterias , invocar o DAO, e tratar o resultado.

pcalcado

Cuidado com isso. Segundo Evans:

Um Repositório é uma ilusão de coleção de objetos, não alguém que responde à queries. Se você quer saber quantos objetos estão dentro de um repositório provavelmente para o cliente isto é apenas um atributo deste, não o resultado de uma pesquisa.

E antes de partir para QueryObject, algo bem inra-estrutura e fora de DDD, siga a suestão do Evans e faça a dobradinha Specificaction/Repository:

Agora se você ainda não leu o livro eu recomendo que o faça antes de tentar qualquer coisa com Domain-Driven Design. Existem muitas fontes de informação completamente distorcidas por aí.

hlegius
"sergiotaborda":
Ah! agora entendi o seu ponto. Vc acha que isso são pesquisas especializadas. Não. São 3 métodos fazendo a mesma pesquisa e retornando informações diferentes dela.

Em um projeto onde vc tivesse a pesquisa "usuáriosAtivos" vc teria

findUsuariosAtivos : List
getFirstusuarioAtivo : usuario
countUsuariosAtivos : int

Para cada pesquisa vc teria 3 metodos. Para N pesqusias vc teria 3N métodos.
Com o outro modelo vc agrupa os métodos em um objeto de retorno. Então vc tem N pesquisas e N métodos.
muito mais simples , não ?

Pera lá então: no seu exemplo temos somente 1 tipo de busca. O que varia são os tipos de retorno. Um Retorna a Collection outro a entidade fora da collection e o outro um número (inteiro) possívelmente.
3 métodos, 1 tipo de busca.
Com isto realmente teriamos muito código duplicado e o problema da manutenabilidade como você disse. Porém, o "outro modelo" que você citou eu não entendi bem como poderia especificar ao método do Repositório que eu quero um getFirstusuarioAtivo ou que eu quero um countUsuariosAtivos.

public class Repositorio {

 public QueryResult findActive(){
          
        // não executa nada
          return new SQLQueryResult (datasource);

 }
}


public class SQLQueryResult <T> implements QueryResult<T>{

   DataSource datasource;

   ListQueryResult  (DataSource datasource{
       this.datasource = datasource;
   }
  
   public  T find (){

         ResultSet rs = execute("SELECT TOP 1  * FROM table ... ");

           if (rs.next){
               return readObject(rs);
          } else {
               return null;
          }

   }

   public Collection<T> list(){

         ResultSet rs = execute("SELECT * FROM table ... ");

         List all = new ArrayList();

          while (rs.next){
               all.add(readObject(rs));
          } 
        return Collections.unmodifiableCollection(all);
  }

  public int count (){
         ResultSet rs = execute("SELECT count(1) FROM table ... ");

         rs.next
              
      return rs.getInt(1);
          
  }

  public boolean isEmpty (){
       return this.count() == 0;
  }
   
}
Veja, no seu exemplo eu usaria o que para dizer ao findActive() que eu quero chamar o find() que me retornará um TOP 1 por exemplo ?
"sergiotaborda":
Não. Nada disso. O QueryObject não faz nada. Ele é apenas um conjunto de dados. Um criteria do hibernate é um QueryObject. O String SQL passado no statement é um QueryObject.

Legal, até aqui eu entendi. No meu pensamento o QueryObject mandava a DAO passear, mas depois da sua explicação eu percebi que ele manda o Repositório passear.
Ao que entendi, o Repositório está para nos servir com Objetos da persistência e agora também evitar um monte de Criteria espalhadas pelas Actions.

Se eu entendi corretamente, uma implementação possível seria:

public class Repositorio {
     
     public Result findAtivos(int porCep) {
           // monta a o critério/query object
           // passa ao criteria a espeficicação de que queremos por cep
           ResultSet rs = usuariosDao.find(Criteria);
           // monta o list/collection ou transmite uma info de não localizada
     }
}

Minha Action chamaria o Repositório - nesse caso acho que não precisaria de Serviço:

public class AlgumacoisaAction {

    public static void main(String..) {
        Repositorio repo = new Repositorio;
        int cep = 12345678;
        repo.findAtivos(cep);
    }
}

Dessa forma eu não exibiria a implementação dos critérios de busca, evitaria um monte de Criterio nas Actions.
Eu apenas não entendi como poderia fazer meu Repositório buscar de formas diferentes uma mesma informação, assumindo o exemplo acima...

public class Repositorio {
     
     public Result findAtivos(int porCep) {
           // monta a o critério/query object
           // passa ao criteria a espeficicação de que queremos por cep
           ResultSet rs = usuariosDao.find(Criteria);
           // monta o list/collection ou transmite uma info de não localizada
     }

     public int countAtivos(int porCep) {
         //monta o critério
         // passa ao critéria o cep
         int rs = usuariosDao.count(Criteria);
         // retorna isto
     }
}
public class AlgumacoisaAction {

    public static void main(String..) {
        Repositorio repo = new Repositorio;
        int cep = 12345678;
        repo.findAtivos(cep);
        int totalAtivoPorCep = repo.countAtivos(cep);
    }
}

Ao que entendi, minha DAO é melhor aproveitada, porém eu teria 3n Métodos no Repositório para n pesquisas especializadas. Justamente o que não é ideal para a manutenabilidade. =(
Pois se eu precisasse fazer uma busca por inativos, eu teria que adicionar no repositório mais 3 métodos. findInativos(); listInativos(); countInativos(); A DAO ficaria com os 3 de sempre, penso.

"sergiotaborda":
Agora, o QueryObject tende a ser mais complexo. A ideia é vc poder pedir aquelas 4 informações de forma combinada algo assim QueryObject q = QueryObject.for(Product.class). where("name").eq(name). and("active").isTrue(). and("selled").in(intervalo)

Ai vc passa para o método find() e pronto. Bácisamente o seu repositorio começa a ter um só método. Só que isso destroi o repositorio. O objectivo do repositorio é manter e conter a contrução do QueryObject

Entendi. A idéia do QueryObject é não ter aquela chuva de pesquisa no Repositório, logo, isso acaba com a utilidade do mesmo. Entretanto, teremos QueryObject esparramadas em todas Actions. Se precisar alterar algo algum dia, ferrou ! Mas vem cá, o hibernate não trabalha exatamente assim ?

"sergiotaborda":
aposto que isso não era um repositorio e sim um DAO. Pela simples razão que os repositorios não recebem QueryObject complexos ( como já expliquei ) e Criteria é um objeto complexo. Isso que vc descreveu é o que o um DAO ou um DomainStore faria.

Sim, era um repositório! Veja na imagem: http://helio.hlegius.pro.br/imagens/oop/repository_de_acordo_com_livro.jpg

"sergiotaborda":
A Criteria que foi enviado ao objeto veio de onde ?
Veio da Action.
"sergiotaborda":
Foi montada por quem ?
Pela Action.
"sergiotaborda":
Quem escolheu os parametros para ela ?
Possivelmente poderia ter sido o usuário utilizando-se da interface selecionou os campos a serem filtrados, porém, novamente a Action que tratou essa informação e criou a criteria.
"sergiotaborda":
Em um sistema como Repositorio , seria o repositorios a fazer isso e a invocar esse objeto que vc descreveu. em um sistema sem Repositorio , básicamente qualquer classe pode invocar o DAO, logo a construção do Criteria está espalhada pelo sistema inteiro. E isso é um problema. Esse é o problema que o repositorio tenta resolver. ele é um cara "acima" do DAO que sabe como criar Criterias , invocar o DAO, e tratar o resultado.
Isso reforça a idéia: repositório ajuda na manutenção das instruções da Dao.
"pcalcado":
Um Repositório é uma ilusão de coleção de objetos, não alguém que responde à queries. Se você quer saber quantos objetos estão dentro de um repositório provavelmente para o cliente isto é apenas um atributo deste, não o resultado de uma pesquisa.
Isso ! No caso, aliás, no meu caso, geralmente eu preciso de um "count()" para retornar informações para paginações ou/e exibir informações adicionais na interface, como por exemplo: "Vendo X registros de um total de 400". Em ambos casos, eu precisaria fazer um find() especializado naquele filtro + um count() para saber quantos são os registros todos (sem filtros).

Neste caso Phillip, o que poderia eu fazer ? Implementar o count() no repositório retornando um "Int" ao que entendi, você não é muito a favor, mas eu ainda não consigo ver outra solução...

"pcalcado":
E antes de partir para QueryObject, algo *bem* inra-estrutura e fora de DDD, siga a suestão do Evans e faça a dobradinha Specificaction/Repository
Seguirei sem dúvidas. Até por que, se eu for criar um queryobject e suas criterias é melhor eu usar algo pronto, como hibernate...
"pcalcado":
Agora se você ainda não leu o livro eu recomendo que o faça antes de tentar qualquer coisa com Domain-Driven Design. Existem muitas fontes de informação completamente distorcidas por aí.
Farei assim que possível. A grande questão é que apenas fora do país tem esse livro disponível, e as lojas nacionais têm um prazo altíssimo para "possível" entrega do mesmo. Isso quebra as pernas =(
sergiotaborda
hlegius:
"sergiotaborda":
Para cada pesquisa vc teria 3 metodos. Para N pesqusias vc teria 3N métodos. Com o outro modelo vc agrupa os métodos em um objeto de retorno. Então vc tem N pesquisas e N métodos. muito mais simples , não ?

Pera lá então: no seu exemplo temos somente 1 tipo de busca. O que varia são os tipos de retorno. Um Retorna a Collection outro a entidade fora da collection e o outro um número (inteiro) possívelmente.
3 métodos, 1 tipo de busca.
Com isto realmente teriamos muito código duplicado e o problema da manutenabilidade como você disse. Porém, o "outro modelo" que você citou eu não entendi bem como poderia especificar ao método do Repositório que eu quero um getFirstusuarioAtivo ou que eu quero um countUsuariosAtivos.

Assim :

QueryResult result = repositorio.findActive();

result.count() // quantos são 
result.find() // o primeiro encontrado
result.list() // todos os encontrados 
result.isEmpty() // encontrou algum ?

if (result.count()>0){  // ou !result.isEmpty()

   for (Produto p : result.list()){
             print(p.getName());
   }

}

repare que result.count() é mais eficiente que fazer o select todo
vc só irá fazer o select em list() se souber que ha alguma coisa para ler. ( isto é um exemplo, no codigo real vc faz o for e pronto)

Outra forma de usar

// todos os produtos
Collection produtos = repositorio.findAll().list();

// quantos produtos 

int quantos = repositorio.findAll().count(); 

// Todos os produtos ativos

Collection produtos = repositorio.findActive().list();

// quantos produtos foram vendidos o mes passado ? 

Month month = Month.current().previous(); // objeto de exemplo só para que se entenda
int quantosVendidos = repositorio.findSelled( month.start(), month.end() ).count();

Repare que um campo só no repositorio não serve. repositorio.count() contao quê ? Isso no máximo informaria o numero total de produtos no repositório mas não o informaria, por exemplo, quantos são ativos ( ou inativos , ou de um certo cliente, ou de uma certa data, etc...). Vc precisa de um count para cada pesquisa. Isso permite automatizar coisas como paginação, por exemplo.

Se eu entendi corretamente, uma implementação possível seria:
public class Repositorio {
     
     public Result findAtivos(int porCep) {
           // monta a o critério/query object
           // passa ao criteria a espeficicação de que queremos por cep
           ResultSet rs = usuariosDao.find(Criteria);
           // monta o list/collection ou transmite uma info de não localizada
     }
}

Minha Action chamaria o Repositório - nesse caso acho que não precisaria de Serviço:

public class AlgumacoisaAction {

    public static void main(String..) {
        Repositorio repo = new Repositorio;
        int cep = 12345678;
        repo.findAtivos(cep);
    }
}

Dessa forma eu não exibiria a implementação dos critérios de busca, evitaria um monte de Criterio nas Actions.

Exactamente isso.

Eu apenas não entendi como poderia fazer meu Repositório buscar de formas diferentes uma mesma informação, assumindo o exemplo acima...

ficaria assim

public class AlgumacoisaAction {

    public static void main(String..) {
        Repositorio repo = new Repositorio;
        int cep = 12345678;
         int totalAtivoPorCep = repo.findAtivos(cep).count();
    }
}

ou

public class AlgumacoisaAction {

    public static void main(String..) {
        Repositorio repo = new Repositorio;
        int cep = 12345678;
         QueryResult qr = repo.findAtivos(cep);
         int totalAtivoPorCep = qr.count();
    }
}
Entendi. A idéia do QueryObject é não ter aquela chuva de pesquisa no Repositório, logo, isso acaba com a utilidade do mesmo. Entretanto, teremos QueryObject esparramadas em todas Actions. Se precisar alterar algo algum dia, ferrou ! Mas vem cá, o hibernate não trabalha exatamente assim ?

O Hibernate funciona exactamente assim porque é uma implementação de DomainStore. Usar QueryObject com DomainStore ou DAO é natural.
Mas isso não significa que vc tem que espalhar as suas Criteria pelos seus Actions. Isso o hibernate não o obriga a fazer.
Vc pode criar um repositorio que chama o hibernate tranqüilamente. (aliás vc deve fazer isso se gosta de codigo mantenivel )

O repositorio que use hibernate é responsável por montar os criteria com base nos parametros e passar ao hibernate.
Implementar um QueryResult baseado no query do hibernate tb é simples, já que o Criteria já implementa os vários métodos diferentes ( ele já otimiza as chamadas da forma que estavamos falando).

"sergiotaborda":
Em um sistema como Repositorio , seria o repositorios a fazer isso e a invocar esse objeto que vc descreveu. em um sistema sem Repositorio , básicamente qualquer classe pode invocar o DAO, logo a construção do Criteria está espalhada pelo sistema inteiro. E isso é um problema. Esse é o problema que o repositorio tenta resolver. ele é um cara "acima" do DAO que sabe como criar Criterias , invocar o DAO, e tratar o resultado.
Isso reforça a idéia: repositório ajuda na manutenção das instruções da Dao.

Exatamente. esse é o papel principal do repositório. Ele tem outras vantagens, mas todas derivam dai.

Só uma ultima coisa : Repositorios podem - devem - ser usados sempre que vc tiver que escrever pesquisas . Seja SQL puro, Hibernate, JPA , sei la... e em nada usa um repositorio significa que vc esteja utilizando a filosofia DDD ou qualquer uma das suas práticas, vc está simplesmente seguindo o Principio de Separação de Responsabildiade, em particular Encapsulamento, que é uma prática comum do OO.

hlegius

sergiotaborda:
hlegius:

QueryResult result = repositorio.findActive();

result.count() // quantos são 
result.find() // o primeiro encontrado
result.list() // todos os encontrados 
result.isEmpty() // encontrou algum ?

if (result.count()>0){  // ou !result.isEmpty()

   for (Produto p : result.list()){
             print(p.getName());
   }

}

repare que result.count() é mais eficiente que fazer o select todo
vc só irá fazer o select em list() se souber que ha alguma coisa para ler. ( isto é um exemplo, no codigo real vc faz o for e pronto)

Outra forma de usar

// todos os produtos
Collection produtos = repositorio.findAll().list();

// quantos produtos 

int quantos = repositorio.findAll().count(); 

// Todos os produtos ativos

Collection produtos = repositorio.findActive().list();

// quantos produtos foram vendidos o mes passado ? 

Month month = Month.current().previous(); // objeto de exemplo só para que se entenda
int quantosVendidos = repositorio.findSelled( month.start(), month.end() ).count();

Ao que entendi, esse findActive() não iria fazer chamada alguma na SQLDao. Apenas montaria o critério de busca. Daí, com o critério pronto, o list() retornaria todos, o count() o SELECT COUNT, o isEmpty() verificaria se não retornou nada e etc… correto ou entendi errado ?

“sergiotaborda”:
O Hibernate funciona exactamente assim porque é uma implementação de DomainStore. Usar QueryObject com DomainStore ou DAO é natural.
Mas isso não significa que vc tem que espalhar as suas Criteria pelos seus Actions. Isso o hibernate não o obriga a fazer.
Vc pode criar um repositorio que chama o hibernate tranqüilamente. (aliás vc deve fazer isso se gosta de codigo mantenivel )

O repositorio que use hibernate é responsável por montar os criteria com base nos parametros e passar ao hibernate.
Implementar um QueryResult baseado no query do hibernate tb é simples, já que o Criteria já implementa os vários métodos diferentes ( ele já otimiza as chamadas da forma que estavamos falando).

Legal. Muitos exemplos que eu vi pela rede, trabalha com Active Records. A implementação com Repositório no caso, seria bem similar a implementação com DAO’s comuns, né ? Digo, apenas precisaria mudar, onde eu chamo as daos, eu precisaria transportar os dados para o Hibernate.

Ufa! Acho que estou começando a sacar a idéia =)

O que por si só é excelente. :smiley:

Abraço e obrigado pela força! Realmente ajudou muito !

sergiotaborda

Correto. Ele prepara o Criteria , cria um QueryResult para esse critério e retorna.
Internamente o QueryResult utiliza o critério para fazer a pesquisa. Ele pode alterar o critério “ligeiramente” para invocar o resultado que ele quer ( por exemplo o find poderia adicionar o comando criteria.setRange(1,1) ( trás só 1, começando no primeiro) - isso vai se tornar um TOP 1, ou um Limit 1 offset 1 no banco)

Objetos que encapsulam chamadas ao Hibernate e encapsulam a criação de critérios são na realidade Repositorios. É que existe essa moda de os chamar de DAO, mas na realiade DAO é outra coisa. Mas , sim, onde vc tem dao chamando o hibernate vc teria repositorios chamando o hibernate

Criado 20 de novembro de 2008
Ultima resposta 26 de nov. de 2008
Respostas 11
Participantes 4