Repository: seu modelo mais Orientado a Objeto

Já tem algum tempo que a excelente discussão no GUJ estava me motivando a escrever a respeito.

Para ambientar, a principal discussão é usar:

Fornecedor fornecedor = ...;
List contas = dao.carregaContasPagasDesde1999(fornecedor);

Ou:

Fornecedor fornecedor = ...;
List contas = fornecedor.getContasPagasDesde1999()

Já que posts na forma de diálogos costumam ser muito interessantes, aproveito um papo que eu e o Paulo Silveira tivemos.

Paulo: credo, achei horroroso isso da classe de domínio acessar o repositório
Paulo: mas fica bonitinha a sentença

Fabio: horroroso pq? vc prefere procedural?
Fabio: repositório é domínio tb
Fabio: um List, um Map são repositórios
Fabio: vc acha acessar List e Map horroroso?

Paulo: tem certa razão
Paulo: mas entao poderia chamar o DAO diretamente de repositório

Fabio: em muitos casos poderíamos
Fabio: eu só estava pensando num jeito legal de injetar

Paulo: desde que o cara não receba os objetos de dominio

Fabio: talvez com interceptor de session, ou listener
Fabio: ou então injeta nos métodos load()

Paulo: então. Complicado um Usuario sempre precisar de Repositorio!

Fabio: todo usuário não
Fabio: só Usuário Managed
Fabio: quando vc dá new num Usuário, vc tb não consegue navegar em relacionamentos
Fabio: só faz sentido pegar relacionamento em managed

Paulo: certo
Paulo: mas olha só
Paulo: vai ter Usuario com repositório e outro sem
Paulo: em alguns vc vai poder chamar o método que faz buscas, em outros não

Fabio: mas isso já é assim

Paulo: não com o dao

Fabio: imagina Usuario @OneToMany Categoria

Paulo: certo

Fabio: vc só consegue chamar usuario.getCategorias() no managed, mesma coisa
Fabio: só vai poder chamar usuario.getCategoriasEspeciais() no managed
Fabio: não adianta dar new Usuario().getCategoriasEspecias();

Paulo: hum… entendi! Podia ser um interceptor como fizemos no caelumweb
Paulo: aí ele setava um atributo privado

Fabio: Ou então o próprio load do dao/repository pode fazer isso.

Paulo: mas sei la, baita arquiteturazinha complicada

Fabio: nada! só injetar o repositório
Fabio: o resto é simples
Fabio: em vez de chamar no dao, que é procedural, chama via getter

Paulo: não não, você tem razão. É facílimo de implementar
Paulo: mas tipo, é muita coisa

Fabio: muita coisa do jeito que estão falando no tópico do GUJ! 500 camadas…
Fabio: Repository não precisa nem ser interface

Paulo: aquele interceptador que fizemos para gravar no lucene já não gosto muuuuito

Fabio: Repository pode ter referência para Session
Fabio: e não precisa injetar o repositorio na entidade com listener/interceptor
Fabio: poderia ser:

public class Repository {
  private final Session session;
  public Repository(Session session) {
    this.session = session;
  }
  
  public void get(Long id) {
    // poderia delegar para um dao, se fosse necessário
    Usuario u =  session.load(id);
    u.setRepository(this);
  }
}

Fabio: ou seja, na hora que você recupera uma Entidade managed, ela já vem com o repositório embutido…

Paulo: dessa maneira dá um pouco mais de responsabilidade para os beanzinhos entidades né? fica legal

Fabio: isso!

Paulo: proximo projeto vamos tentar essa abordagem?

Fabio: demorou… 😉
Fabio: hoje em dia, se vc quiser mostrar um objeto e alguma pesquisa personalizada, vc tem que consultar o objeto e a lista nas lógicas
Fabio: separados! E aí ejetar os dois
Fabio: muito feio
Fabio: geralmente faz-se isso (código usando algum controlador ruinzinho):

public class Logica { // extends Action? 😉
  public void mostraFornecedorEContasPagas() {
    Long id = request.getParameter("id"); // século passado...
    Fornecedor f = fornecedorDAO.load(id)
    List<Conta> contasPagas = contasDAO.listaContasPagasDoMes(fornecedor);
    // ejeta tudo para mostrar na view. No século passado seria:
    request.setAttribute("fornecedor", fornecedor);
    request.setAttribute("contasPagas", contasPagas);
  }
}

Fabio: MUITO procedural!

Paulo: cara se aplicarmos essas idéias, com velocity isso ia ficar ANIMAL
Paulo: Ia dar para chamar os métodos a la DAO direto no beanzinho

Fabio: ISSO!

Paulo: #foreach contas in contas.desde(1994)

Fabio: é meu, legal né?
Fabio: com JSP EL dá tb

Paulo: mais ou menos, teriamos de mexer nos evaluators

Fabio: não, soh usar padrão javabean
Fabio: ahhh vc quer passar parâmetro, aí EL não rola mesmo
Fabio: mas é puro DDD, já fiquei pensando bastante sobre o assunto
Fabio: ultimamente que tenho enxergado um jeito legal de aplicar

Paulo: bacana
Paulo: acho q vale a gente testar
Paulo: parava de ficar enfiando getters para expor as coisas para view.
Paulo: FABIO TODO: blogar sobre isso

Feito! 😉

28 Comentários

  1. xebe 13/06/2007 at 15:41 #

    Fabio, excelente este post.
    Como ficaria sua classe Fornecedor seguindo este seu modelo?
    Obrigado.

  2. Daniel Souza 14/06/2007 at 14:59 #

    é, gostaria de saber tbm
    como ficaria Fornecedor ??

  3. Fabio Kung 15/06/2007 at 12:58 #

    Recomendo que vocês leiam o tópico do guj (link no começo deste post) na integra. Tem várias alternativas de como ficaria o Fornecedor. Uma alternativa poderia ser:

    public class Fornecedor {
      private final FornecedorRepository repo;
      public Fornecedor(FornecedorRepository repo) {
        this.repo = repo;
      }
      public List getContasDesde(Date date) {
        return repo.getContas(this, date);
      }
    }

    Eu sei que é simplista demais. Esse é apenas um dos jeitos.

  4. Felipe Regalgo 21/06/2007 at 21:32 #

    E se eu quisesse obter uma lista de todos os Fornecedores do sistema??
    esse metodo ficaria dentro da classe Fornecedor?? tipo:

    public class Fornecedor {
    private final FornecedorRepository repo;

    public Fornecedor(FornecedorRepository repo) {
    this.repo = repo;
    }
    public List getTodosFornecedores() {
    return repo.getTodosFornecedores();
    }
    }

    vc não acha melhor que essa funcionalidade não exista na classe Fornecedor e seja acessada chamando diretamente do repositorio (que faz parte do dominio)? por exemplo:

    fornecedorRepository.getTodosFornecedores();

    ao inves de

    fornecedor.getTodosFornecedores();

    a final de contas estamos querendo saber uma informação especifica de todos os fornecedores, e não daquele especifico!!!

    VAleu,
    e parabéns pelo post

  5. Paulo Silveira 21/06/2007 at 22:29 #

    Felipe, ai voce usa o dao mesmo. Esse esquema é para deixar o objeto mais poderoso e com mais responsabilidade, mas nao faz sentido colocar nele um método de instância com cheiro de estático….

  6. Fabio Kung 21/06/2007 at 22:40 #

    Boa Felipe,

    Essa é a velha discussão sobre ActiveRecord x DataMapper, mas nesse caso (e em Java) eu faria repository.getTodosFornecedores().

  7. Alexandre F. da Silva [afsrj] 21/07/2007 at 08:05 #

    Eu gostei e nao gostei.
    So vale se for para o mesmo objeto(instância, this!) se for pra trazer coisas do tipo, fornecedor.getContasPagar() trazer de uma outra instância porque voce permitiu no conta a pagar pegar o getId, e liberou o acesso do setId ao inves de ser pelo construtor.

    Juro que vomitaria!

  8. Leila 30/01/2008 at 14:59 #

    Boa sorte Juliano! vai em frente! vc é um cara de futuro ainda! hehehehe

  9. Emerson Moretto 12/02/2008 at 05:06 #

    Eae Fabin!!

    parabéns pelo post cara!! Excelente! Ultimamente estive lendo o livro DDD do Eric Evans… e esse seu tópico veio a complementar muita coisa… talvez não tenha resolvido meus problemas, mas provavelmente tenha me trazido mais dúvida rsrsrs! (e isso é muito bom!)

    Estou implementando um projeto lá no LSI dessa forma que você sugeriu.. ou pelo menos uma tentativa do repositório injetado automaticamente ao entrar no estado “managed”.

    mais uma vez, parabéns!! você ta cada vez mais prodígio!! rsrsr
    abraços

  10. Dirceu 16/09/2008 at 05:27 #

    Eu até agora não sei o que é exatamente um Repositório.
    Seria uma espécie de ligação entre o DAO na camada de persistência e o domínio?? E não tenho a menor idéia de para que ele serve!

  11. Rômulo 21/07/2009 at 11:11 #

    E se eu tiver 2 implementações para o DAO (JDBC e Hibernate) mas quiser isolá-las usando o Repository, ficando assim transaparente para o cliente, como faria para que minha aplicação tivesse esse suporte?

    Eu consigo fazer uma interface em comum para os dois, mas na hora de injetar como faria para dar suporte as 2 implementações? Porque no meu caso é possível que o cliente prefira usar, em algum momento, o JDBC porém o padrão seria Hibernate.

  12. Rômulo Augusto 08/07/2011 at 09:58 #

    Tópico antigo, mas tenho uma pergunta que não vi nos comentários:

    O que você acha de também, além do construtor, um método específico receber um repositório como parâmetro?

    Por exemplo, para o caso do fornecedor:
    forncedor.getConstasDesde(1994, fornecedores);

    onde “fornecedores” seria o repositório.
    Isso seria legal para o caso em que o Hibernate cria os objetos Fornecedor. Assim não me preocuparia em alguma forma de injetar nessa criação.

    O que acham?

  13. Junior Pires 29/09/2012 at 14:19 #

    Fabio, desculpe a ignorância, mas não entendi muito quando essa parte :

    Paulo: então. Complicado um Usuario sempre precisar de Repositorio!
    Fabio: todo usuário não
    Fabio: só Usuário Managed

    O que seria esse “managed”?
    Pergunto porque tenho a mesma indagação de Paulo : terá momentos que eu irei querer criar um objeto SEM um REPOSITORY. Então os métodos que utilizam o repositorio ficariam lá inúteis? E se eu chamá-los? NullPointsExceptions?

    Obrigado pela atenção

  14. Átila Silva 29/11/2018 at 23:57 #

    Júnior Pires essa é a mesma indagação que eu tenho. Ela deve valer seis milhões de dólare. Se tratando de uma aplicação que cresça muito é interessante abordar a capacidade de propagar boas alterações que os repositórios podem fazer com listagens e recuperação de objetos no banco. Os ORMs podem oferecer um contrato para implementação de abstrações de métodos de persistências.

Deixe uma resposta