Abstrair ou não os repositórios

Em todas as minhas aplicações tento abstrair os repositórios com interface + implementação. Por exemplo, tenho uma interface UserRepository e uma implementação JpaUserRepository. Assim uso na minha camada de negócio apenas a UserRepository.

Dessa forma eu posso futuramente matar a classe JpaUserRepository e usar uma LdapUserRepository e a camada de negócio nada muda. Além do mais usando essa idéia eu somente pelo nome já sei se tal classe usa JPA, LDAP ou qualquer outra coisa…

O que vocês tem usado para os repositórios? Há realmente um ganho quando a essa abstração ou estou fazendo algo desnecessário? Obviamente sempre me pareceu uma boa idéia, porém meu projeto cresceu tanto que penso se vale a pena escrever o dobro de classes para os repositórios.

Você vai utilizar essa separação de implementação que descreveu acima ?
É muito custosa a separação?
É muito complexa?

Se não ?
Para que fazer isso ?
:twisted: :twisted: :twisted:

mateusbrum, não entendi muito bem suas perguntas.

De qualquer forma estou usando isso atualmente em um sistema meu. Complexo não é não, porém como o sistema cresceu muito, achei que eu poderia encurtar um pouco o assunto. Porém eu não queria perder o desacoplamento entre ambas camadas. E minha indagação é se realmente é válido eu não possuir acoplamento entre elas.

Essa implementação lembra muito a arquitetura proposta para a camada DAO no Hibernate in Action. Em particular acho bem interessante, pq usa bem o conceito de programar voltado a interface, e isso é muito bom para manutenção, teste unitário e etc. O Guerra, editor da Munda Java, me falou a algum tempo, qdo me orientou no TCC, que quando vc começa a usar interfaces é quando realmente vc está programando em OO, já que está conseguindo abstrair as coisas. Em particular, eu concordo com ele

A utilidade obvia que vejo para isso é nos testes, para com a idéia de que “posso precisar mais tarde” é furada.

Quando ele disse isso, ele quis dizer “voltado ao contrato (abstração)”, isso não quer dizer que tu tem que meter interface em tudo que vê.
Lembre-se que não é apenas a interface que garante um contrato. :slight_smile:

[quote=garcia-jj]Em todas as minhas aplicações tento abstrair os repositórios com interface + implementação. Por exemplo, tenho uma interface UserRepository e uma implementação JpaUserRepository. Assim uso na minha camada de negócio apenas a UserRepository.

Dessa forma eu posso futuramente matar a classe JpaUserRepository e usar uma LdapUserRepository e a camada de negócio nada muda. Além do mais usando essa idéia eu somente pelo nome já sei se tal classe usa JPA, LDAP ou qualquer outra coisa…

O que vocês tem usado para os repositórios? Há realmente um ganho quando a essa abstração ou estou fazendo algo desnecessário? Obviamente sempre me pareceu uma boa idéia, porém meu projeto cresceu tanto que penso se vale a pena escrever o dobro de classes para os repositórios.[/quote]

A sua ideia está correta em essência, mas tecnicamente isso ai não são repositorios.

A diferença é que o repositorio depende apenas do dominio. Então o seu UserRepository é um repositorio mesmo. O nome do repositorio é construido como [NomeDaEntidade]Repository. O JpaUserRepository e o LdapUserRepository são estratégias para o repositorio. eles poderiam-se chamar JpaStrategyUserRepository. A diferença é que o nome deles é montado com o nome da tecnologia. Ou seja, algo que não é do dominio.

Se vc faz JpaUserRepository herdar/implementar UserRepository vc não está usando o padrão Repositorio como deve ser. Vc está usando Strategy na realidade (“uma interface várias estratégias de acesso”). Vc precisa que a estratégia seja plugável no repositorio. Desta forma vc usa sempre o mesmo objeto, e pode mudar a estratégia depois, sem usar herança. Ou seja, vc sempre injeta UserRepository onde precisar, e injeta a estratégia de acesso no repositorio ao inves de injetar a estratégia diretamente onde é usada.

É por isso que sempre digo que repositorios não são interfaces.

Programar para interfaces(o conjunto de métodos públicos de uma classe) não é a mesma coisa que criar um interface(a palavra reservada) em Java e sair criando Xpto e XptoImpl (Ou IXpto e AlgumaCoisaXpto) pelo sistema todo.

Programar para interfaces significa você ler a documentação do método, e se basear somente nisto para desenvolver os seus programas. O que tem dentro do corpo dos métodos deve ser apagado da tua mente.

Sobre abstrair ou não o repositório, eu digo que isto é indispensável para testes, e na prática só para testes. Digo no sentido que ninguém é maluco de trocar o Oracle por outro banco relacional. Não quer dizer que você deve praticar um vendor lock-in ativamente, boas práticas de programação sempre valerão acima de tudo, mas deixe este problema para a hora em que se tornar um problema, no melhor estilo YAGNI.

[quote=Bruno Laturner]
Sobre abstrair ou não o repositório, eu digo que isto é indispensável para testes, e na prática só para testes. Digo no sentido que ninguém é maluco de trocar o Oracle por outro banco relacional. Não quer dizer que você deve praticar um vendor lock-in ativamente, boas práticas de programação sempre valerão acima de tudo, mas deixe este problema para a hora em que se tornar um problema, no melhor estilo YAGNI.[/quote]

O problema de seguir este conselho é que é caro. Quando vc implanta um sistema e descobre que o Oracle que estava nos requisitos é o 7 e não o 10 como vc pensou, ou pior, quando lhe disseram que era Oracle, mas esqueceram de lhe dizer que mudaram param para MYSQL entretanto.

Sempre, sempre uma boa arquitetura é aquela que está preparada para mudar. Isto não significa que vamos implementar todos os XPTOImmpl que nos lembremos, mas sim que vamos deixar o sistema desacoplado. Desacoplar passar por diferentes coisas e desenhar para interfaces é apenas uma delas.

Um artigo muito bom na JavaMagazine deste mes do Osvaldo Doederlein reflete exactamente isto.
Saber abstrair é necessário para saber desacoplar. E saber desacoplar é necessário para você não passar horas refactorando um código que vc não mais lembra o que faz.

[quote=sergiotaborda]Se vc faz JpaUserRepository herdar/implementar UserRepository vc não está usando o padrão Repositorio como deve ser. Vc está usando Strategy na realidade (“uma interface várias estratégias de acesso”). Vc precisa que a estratégia seja plugável no repositorio. Desta forma vc usa sempre o mesmo objeto, e pode mudar a estratégia depois, sem usar herança. Ou seja, vc sempre injeta UserRepository onde precisar, e injeta a estratégia de acesso no repositorio ao inves de injetar a estratégia diretamente onde é usada.

É por isso que sempre digo que repositorios não são interfaces.
[/quote]

Sérgio, entendi bem a tua idéia. Nesse caso fiz mesmo um merge entre repository e strategy. Vou rever isso.

Bruno, na verdade essa coisa de trocar de banco é uma “lenda urbana” mesmo, concordo com você. É tão pouco provável que você troque de banco de dados que nem vale a pena fazer isso. Porém a minha intenção é de esconder para as camadas de serviço quem é meu repositório. Posso ter um JPA ou até mesmo acesso ao filesystem sem que as classes de serviço sintam a diferença, ou seja, qualquer que seja o repositório ele irá respeitar a sua interface (mesmo que seja 1 interface para 1 implementação).

Totalmente de acordo.

.

[quote=garcia-jj]mateusbrum, não entendi muito bem suas perguntas.

De qualquer forma estou usando isso atualmente em um sistema meu. Complexo não é não, porém como o sistema cresceu muito, achei que eu poderia encurtar um pouco o assunto. Porém eu não queria perder o desacoplamento entre ambas camadas. E minha indagação é se realmente é válido eu não possuir acoplamento entre elas.[/quote]

Se vc não quer perder o tal desacoplamento, sua pergunta foi respondida por vc mesmo. Eu particularmente prefiro 1000x código reduzido do que código abstraído/desacoplado só por algum prazer em abstrair/desacoplar.

Ressuscitando o tópico, essa semana saiu no blog da Caelum um artigo sobre repositórios. http://blog.caelum.com.br/2010/07/26/possibilidades-de-design-no-uso-do-seu-generic-dao/

Nesse artigo os repositórios possuem interface e implementação, unidos as DAOs.

Garcia,

Utilizo uma única camada na implementação do repositório, que por sua vez, usa um DAO. Esse DAO possui uma implementação diferente para cada tecnologia de persistência. Há um sistema que roda em dispositivos móveis que quando fica off-line, precisa persistir utilizando o sistema de arquivos do dispositivo. Troca-se o DAO e a vida segue.