Possibilidades de design no uso do seu Generic DAO

Muitas vezes, quando estamos criando nosso sistema temos a tentação de criar o GenericDAO para não ter que ficar repetindo as operações CRUD e listagens.

O maior problema com o GenericDAO é que não necessariamente todas as operações fazem sentido para uma determinada classe. Daí o que fazer se, por exemplo, não faz sentido excluir um pagamento?

public class PagamentoDAO 
          extends GenericDAO<Pagamento> {
    
     @Override
     public void excluir(Pagamento pagamento) {
         throw new UnsupportedOperationException();
     }
}

Não parece uma solução muito elegante, mas é um dos únicos jeitos de proibir uma operação declarada na classe mãe, e ainda assim, só funciona em tempo de execução. Esse é um dos principais motivos para muitos não gostarem de usar o GenericDAO e preferirem usar composição ao invés de herança. Mas como fazer para não repetir o código trivial das operações do CRUD?

Um dos jeitos é usar uma outra abstração de persistência de objetos: o Repository. Com o Repository, temos um lugar onde podemos guardar e buscar por objetos, não importando como fazemos isso. O DAO já está muito ligado com armazenamento em banco de dados, e foi criado quando as operações do BD eram muito trabalhosas (em especial no JDBC).

E como juntar o Repository com o GenericDAO? O Repository pode ser definido, por exemplo, como uma interface, e aí podemos fazer o seguinte: se, para um pagamento, faz sentido apenas salvar e listar, mas não excluir, então criamos a interface:

public interface PagamentoRepository {
    void salva(Pagamento pagamento);
    Pagamento busca(Long id);
    List<Pagamento> lista();
}

E usamos o GenericDAO como implementação dessa interface:

class PagamentoDAO 
          extends GenericDAO<Pagamento> 
          implements PagamentoRepository {
   // implementacao extra
}

E no nosso código de domínio “nunca” referenciaremos o PagamentoDAO, apenas o PagamentoRepository, assim mesmo que a implementação saiba fazer mais coisas, a interface só expõe as operações suportadas.

Se você usa Injeção de Dependências e algum framework que a suporta (como o VRaptor, Spring ou Java EE6), você pode deixar os DAOs apenas como infraestrutura, e usar os Repositories como interfaces públicas da sua aplicação:

public class PagamentoController {
    public PagamentoController(PagamentoRepository repository) {
         this.repository = repository;
    }

    public void salva(Pagamento pagamento) {
         // validações e outras regras
         repository.salva(pagamento);
    }
}

Ainda poderíamos melhorar o nome do nosso repositório para BaseDePagamentos, ContasAPagar ou ainda Pagamentos, evitando usar sufixos nas classes. Usando abstrações e padrões simples conseguimos evitar repetição de código sem perder a semântica e restrições das nossas classes de modelo, além de esconder detalhes de implementação.

22 Comentários

  1. Alexandre Gazola 26/07/2010 at 22:22 #

    Muito bom o post! Mostra como usar interfaces como “funis” para o que queremos “enxergar” no sistema. Essa implementacao pode ser uma alternativa ao Generic DAO por composicao, que tem a desvantagem de acabar gerando os varios metodozinhos wrapper.

    abraços

  2. Edufa 26/07/2010 at 23:01 #

    Eu gosto de usar o repositorio no plural – Pagamentos – quando faz sentido. E estava hoje mesmo pensando em como melhorar esta relação entre as classes, gostei bastante da ideia apresentada e irei testa-la aqui.

  3. Luciano 27/07/2010 at 08:48 #

    Bom post. Mas IMHO o primeiro exemplo (PagamentoDAO) deveria também usar uma interface (PagamentoDAO) e uma classe de implementação. Ai só seria questão de nomes, o que não deixa de forma alguma de ser um ponto importante.

  4. Rafael Duque Estrada 27/07/2010 at 14:02 #

    Mostra o valor de programarmos orientados a interface, utilizando o padrão Repository para essa finalidade.

  5. Wagner 28/07/2010 at 12:11 #

    Oi Lucas, parabéns pelo post. Solução elegante para um problema do dia. É só lembrar, como disse o Rafael Duque, que é sempre bom trabalhar orientado a interfaces.

    Com um mecanismo de Injeção de Dependência as coisas ficam melhores ainda.

    []s

  6. Lucas Cavalcanti 28/07/2010 at 14:00 #

    @Rafael Duque, @Luciano e @Wagner
    A idéia desse post é justamente programar orientado a interfaces…
    PagamentoDAO é apenas a implementação, mas o que vamos usar nas outras classes é a interface: PagamentoRepository…(ou Pagamentos, ou ContasAPagar, etc)

    o resto da aplicação não precisa saber que existe um PagamentoDAO, só o PagamentoRepository, que é uma interface.

  7. Washington Botelho 28/07/2010 at 23:31 #

    Ótimo post Lucas,

    Esse assunto é muito interessante, inclusive é um dos meus rascunhos.
    Costumo também criar um GenericRepository, assim todos os meus outros repository o estedem, evitando a repetição das assinaturas do CRUD.

    Parabéns pelo post. (:

  8. Lucas Cavalcanti 28/07/2010 at 23:45 #

    Cuidado: criar um GenericRepository (interface) cai no mesmo erro de criar o Generic DAO — vc vai ter todas as operações definidas mesmo que nem todas façam sentido.
    A idéia desse post é justamente criar a interface XYZRepository para restringir essas operações e só expor o que faz sentido para a entidade/classe XYZ.

  9. Flavio Duarte 01/08/2010 at 13:05 #

    Post interessante, deixa bem claro a diferença entre o DAO’s e repository fazendo um bom uso deles

  10. Rafael Ponte 03/08/2010 at 22:56 #

    Mais um excelente post vindo da Caelum. Parabéns Lucas!

    Eu não gosto muito da idéia de GenericDAO ou qualquer estratégia nesse aspecto. Isso me faz lembrar classes ao estilo OptimusPrimeDAO super inchadas de métodos e muita responsabilidade.

    Para cenários de CRUDs isso é mais que comum, e se for para fazer ao menos se busque melhores práticas para isso, e daí seu post cai como um luva.

    Enfim, programar voltado a interfaces é a melhor que dica que você poderia ter dado aos leitores no post.

    Parabéns de novo.

  11. Marcus Cavalcanti 11/08/2010 at 13:29 #

    Bacana o post.. essa é uma abordagem interessante que o DDD custuma usar.

    Você disse no post, que o DAO geralmente está associado a BD, mas acho importante frisar que não necessariamente isso ocorre.

    Por exemplo, eu tenho algumas aplicações que acessam/recuperam/armazenam dados através de web services, arquivos, etc… nesse caso o correto não seria referenciar isso dentro do próprio DAO? Usando o próprio conceito de DI poderíamos injetar as classes necessárias para acesso a WS ou Arquivo dentro do DAO, além do próprio BD, que no caso está no GenericDao.

    Nesse caso manteríamos o repositório sendo apenas uma interface e quem precisar recuperar/acessar/gravar dados, por exemplo, controllers e services (service layer), não saberiam onde isso estivesse armazenado, usariam apenas o repositório e assim teria acesso aos dados independente daonde eles estejam armazenados.

    Gostaria de saber se a maioria usa dessa maneira ou se usam de uma maneira diferente que gostariam de compartilha.

    Esse assunto levantado é bem interessante.

  12. Lucas Cavalcanti 11/08/2010 at 14:04 #

    Você está certíssimo, Marcus… o DAO não necessariamente usa banco de dados…

    esse post é voltado para aqueles que usam DAOs de BD e querem usar o GenericDAO para não ficar repetindo código do CRUD em vários graus…

    eu tenho evitado ultimamente o uso de sufixos (DAO, Repository, Factory, etc) nas classes, e usado nomes mais ligados ao domínio mesmo…

    mas excelente observação =)

  13. fidencio 03/12/2010 at 18:32 #

    Lucas,

    Levando em conta que Repositorio é um padrão de projeto e posso afirmar que ele:

    +não é mutável
    +se não é mutável porque possui especificação? o pessoa costuma usar criar uma interface chamada XPTORepository que é implementada por um DAO ou por uma classe XPTORepositoryImp, que é o caso do exemplo de DDD do evans, ele não usa DAO,.

    +Já vi no GUJ pessoas dizendo que JPA e suas implementações fazem papel de DAO, ou nada mais é que DAO, então não faz sentido eu embrulhar um Session do Hibernate ou um EntityManager da JPA dentro de uma classe de DAO concorda?

    +Se o os metodos add, remove da interface EntityManager faz todo trabalho, posso afirmar que EntityManager é um repositorio ? ou poderia se um repositori o? nesse caso eu Delegaria operações com sgbd de dentro da minha Entity mesmo?:

  14. Lucas Cavalcanti 03/12/2010 at 20:39 #

    Olá fidencio,

    -o que vc quis dizer com o Repositorio não ser mutável? De só existir uma implementação sempre? A motivação de fazer relacionamentos entre classes usando interfaces ao invés classes concretas é para diminuir o acoplamento. Isso facilita os testes e também melhora o uso do nome.
    Nas interfaces eu posso colocar coisas mais representativas, por exemplo criar a interface Biblioteca para ser o repositório de livros, e implementá-la com a classe LivroDAO por exemplo.

    -EntityManger e Session são DAOs mas embora seja quase ok espalhá-los pelas suas classes para executar os métodos de CRUD, não é muito bom espalhá-los para fazer queries. A gente encapsula o EM e a Session por outro motivo: é uma boa prática depender o mínimo possível de classes que vc não pode mudar o código fonte. Então os AsteriscoDAOs existem também para limitar a dependência com a API do hibernate/jpa.

    – Não dá pra chamar o EntityManager de Repository pq ele é genérico, não tem nenhuma semântica envolvida. A idéia dos Repositories é eles serem bastante específicos e carregarem a semântica do domínio.
    Você pode colocar as operações de BD para a entity, mas isso é independente do Repository, faz parte de outro pattern: o ActiveRecord.

    []’s

  15. Marcio 30/12/2010 at 10:52 #

    Lucas, parabéns pelo post, comentário meio atrasado… Mas o que você acha da abordagem de ao invés de criar uma interface Repository para cada entidade, criar interfaces para marcar os DAOs com determinadas operações de acesso a dados? Por exemplo:
    package com.teste;

    public class LivroDao implements Insertable {

    private GenericDao dao;

    public LivroDao() {
    dao = new GenericDao();
    }

    @Override
    public Livro insert(Livro t) {
    return dao.insert(t);
    }

    }

    Assim eu marcaria esse DAO com a capacidade de apenas inserção… e então existiriam interfaces para marcar as outras operações… já a implementação ficaria a cargo do GenericDAO, creio que esta abordagem evitaria a criação de diversas classes AsteriscoRepository… gostaria de saber sua opnião…

  16. Lucas Cavalcanti 04/01/2011 at 01:22 #

    Olá Marcio,

    não me parece uma boa abordagem criar uma interface por operação, pois a gente não ganha muita coisa com isso (além de forçar a criação do método).
    Ficaria estranho fazer algo assim no código:

    Insertable dao = new LivroDao();

    ou ainda receber um Insertable como parâmetro de um método.
    Criar uma interface sem que ela seja usada para referências ou polimorfismo, na minha opinião, não é uma boa prática em geral.

    O que eu considero boa prática é programar voltado a interfaces, assim você só se acopla somente a “o que” a classe promete fazer e não a “como” ela vai fazer. Criar várias interfaces *Repository (ou nomes melhores como Livraria ao invés de LivroRepository) não é um desperdício, você ganha em semântica (abstração melhor) e diminui o acoplamento favorecendo o polimorfismo (posso usar qualquer implementação, inclusive uma falsa – para testes).

    Abraços

  17. Rogério Queiroz 10/11/2011 at 18:46 #

    E sobre a questão de fechar a session…
    Sempre depois de cada operação ou, no caso de aplicações java SE, seguir o conceito de openSessionOnView?

  18. rafael 16/10/2013 at 11:01 #

    Fiz como no blog xcriei um generic Dao com métodos genéricos de crud uma interface que implementa métodos específicos da classe . por ex:

    public class ProdutoDAO extends DataAccessService implements
    Repositorio {

    public ProdutoDAO() {
    super(Produto.class);

    }

    A interface :

    public interface Repositorio {

    public Produto guardar(Produto produto);

    Ocorre que quando no meu CadastroProdutoBean chamo o repositório só mostra os métodos implementados e quando chamo o ProdutoDAO vejo todos os métodos tanto da interface quanto do genericDao.

    utilizo no Bean com CDI: e desta forma vejo todos os metodos

    @Inject
    private ProdutoDAO produtos;

    desta abaixo so vejo os da interface:

    @Inject
    private Repositorio produtos;

    Como no código de domínio referenciar somente o repositório?
    Tem alguma ideia pois pelo que entendi não é isso o proposto.

    Grato.

  19. André Valenti 12/11/2013 at 10:41 #

    Olá, Lucas,

    Eu não sei se eu entendi bem o post. O cerne da questão é preferir composição a herança, evitando publicar métodos que não façam sentido, como o excluir do PagamentoDAO? Se for simplesmente isso, eu entendi.

    É que, no meu entendimento, o padrão DAO não presume necessariamente um banco de dados real. Se pensarmos dessa forma, não vejo diferença entre DAO e Repository.

  20. Anderson Araújo 09/04/2015 at 18:08 #

    Desculpe-me pelo posto atrasado, mas não pude deixar de fazer uma pequena observação: você utilizou o pattern Repository (interface) para expor operações, sendo que a implementação dessa é feita por outro pattern (DAO). No meu ponto de vista, batava criar uma interface IPagamentoDAO (por exemplo) e resolveria o problema da mesma forma, haja vista que DAO é um pattern baseado em interfaces, não havendo necessidade de usar o Repository. Claro, a grande questão aqui é não usar a implementação do pattern (repository/DAO), mas sim as respectivas interfaces.

Deixe uma resposta