Há algum problema em os objetos de negócio acessarem diretamente os repositórios? Ou isso é normal?
Ex:
public class Cliente {
public ContasPagar[] listarContasPagar() {
RepositorioContasPagar repositorioContasPagar = // obtem o repositorio de alguma forma...
return repositorioContasPagar.listarPorCliente(this);
}
}
Se não for o próprio modelo quem obtém o repositório de fato (ou seja, se usar injeção de dependência), então não vejo problema. Apenas se um dia você for reusar o modelo, o seu esquema de repositório pode não casar com o novo cenário: você pode precisar alterar ou remover essa “expressão” do repositório dentro do modelo. Se é um “controlador” quem coloca e retira os objetos do repositório (ou seja, o modelo em si não conhece o que é um repositório), o modelo fica mais desacoplado, mas acho improvável que isso faça diferença no seu caso.
[quote=magnomp]Há algum problema em os objetos de negócio acessarem diretamente os repositórios? Ou isso é normal?
Ex:
public class Cliente {
public ContasPagar[] listarContasPagar() {
RepositorioContasPagar repositorioContasPagar = // obtem o repositorio de alguma forma...
return repositorioContasPagar.listarPorCliente(this);
}
}
[/quote]
Olá o padrão Repository, é um conceito de negocios, ou seja como a sua classe Cliente tbm faz parte do Negocio, não há problema alguma em sua classe Cliente acessar o Repository.
Pessoal, obrigado pelas respostas… Era o que eu pensava tambem, só queria mesmo confirmar.
Agora, andei pensando, e acho que esse meu exemplo não foi muito feliz, né? Porque se eu já tenho um repositório para contas a pagar que me permite fazer uma consulta por cliente, porque repetir isso na classe Cliente? Acho que seria um acoplamento desnecessário…
Eu acho que seu exemplo é legal. Fica mais natural cliente.listarContasPagar() em vez de repositorioContasPagar.listarPorCliente(cliente), ou ainda ContasPagar.listarPorCliente(cliente).
Só tenham cuidado, pois na maioria das vez que uma entity precisa acessar um repositório diretamente, significa algum erro de entendimento do domain.
Imagine que a Entity precisaria de um método que recebesse o repositório, quando deveria receber uma lista de outras entities. Nesse caso, trata-se de uma operação mais complexa, que talvez deva ser encapsulada por um service, injetando o repositório no service, que se encarrega de obter a lista e chamar o método.
Além disso, no exemplo citado, talvez pelo nome inexpressivo dos métodos, não vejo qual a relação entre o Cliente e as ContasAPagar. Talvez se fosse um método do tipo, cliente.contasPendentes() seria melhor. Aí sim, eu entenderia que esse método retorna uma lista de contas pendentes do cliente. Nesse caso, cliente já teria uma lista de contas pendentes associadas a ele e dessa forma não precisaria do repositório.
O problema aqui é que repositórios dependem das entidades, se alguma entitdade conhece repositório é porque existe uma referência cruzado. Não iria tão longe em dizer que isso significa um erro de entendimento do domain. No máximo uma prática controversa em design OO devido aos problemas conhecidos que referências cruzadas podem causar na manutenção dos objetos.
Na verdade afirmações vazias como essa apenas fazem as pessoas acreditarem que vão encontrar erros no domain procurando por entidades usando repositórios e esquecem de todo resto. Tb não esqueça que DDD não é solução para tudo, pelo contrário, é overkill na maioria dos casos.
O problema aqui é que repositórios dependem das entidades, se alguma entitdade conhece repositório é porque existe uma referência cruzado. Não iria tão longe em dizer que isso significa um erro de entendimento do domain. No máximo uma prática controversa em design OO devido aos problemas conhecidos que referências cruzadas podem causar na manutenção dos objetos.
[/quote]
Não só pela referencia circular. Eu vejo um problema de expressão do Domain, que pode significar problema de entendimento do Domain. Obviamente, digo isso pois já vi casos como esse várias vezes. Também mencionei que isso acontece sim na maioria das vezes, mas não todas as vezes.
Levando na esportiva… Concordo que uma afirmação dessas pode confundir aqueles que tem menos experiência e que estão buscando uma receita de bolos. Mas para aqueles que são bons entendedores, não vejo problemas.
O fato é que seria difícil enumerar as diversas possíveis situações imagináveis, por isso, deixo a minha opinião que é baseada em minha experiência. E reforço a idéia que cada um deve analisar o domínio em que está inserido.
No caso de dúvida, recorra ao Domain Expert para tentar esclarecer, já que estamos tratando de Domain Driven Design.
Bom independente disso, respondi sobre o ponto de vista do DDD pois é sobre o que trata o thread.
Só uma nota: Concordo que não é solução pra tudo, mas não tenho tanta certeza de que seja overkill na maioria dos casos. Talvez 50% 50%? Não sei. Mas concordo que DDD não serve para muitas equipes, por questão de postura dos desenvolvedores. Mas isso é assunto pra um outro post.
Deixando um pouco de lado os últimos termos da moda, não acho que deixar os objetos de negócio cientes da camada de persistência necessariamente indique um entendimento errado do domínio. Do contrário o que dizer (voltando aos termos da moda) das aplicações usando ActiveRecord? Estariam todos com prováveis problemas de modelagem? A persistência dos objetos é um problema de implementação da aplicação, e não do domínio, e ao meu ver, é possível sim um mesmo domínio ser implementado com uma abordagem ou outra.
Olá renato,
você usará ActiveRecord para aplicações mais simples de CRUD, ou quando for o caminho técnico mais fácil e você nõa quiser abrir mão disso.
Quanto ao caso dos frameworks, eu concordo que às vezes é difícil de manter. Em casos como o Rails/Grails por exemplo, é uma opção ignorar o uso de repositórios, mas aproveitar outros conceitos do DDD. Eu faço isso frequentemente.
Isso não quer dizer entendimento errado do domínio. Concordo.
O que eu disse lá em cima, foi que se em um caso onde você opte por usar DDD e Repositórios, há situações em que chamar o repositório direto é o correto. Há situações em que o ideal seria colocar isso dentro de um service. O desenvolvedor decide.
O detalhe do entendimento do domínio que mencionei foi que muitas vezes, muitas mesmo, nos deparamos com conceitos implicitos dentro do domain sem perceber. A necessidade de uso de um repositório dentro das entities pode ser um sintoma de conceito implicito. Logo há a necessidade de averiguar e conferir o domain. Nesse caso, basta um pequeno contato com o domain expert para verificar se não se trata de uma operação mais complexa, que envolva muitas entities diferentes, ou que seja composta de operações diferentes. Se for o caso, cria-se tal operação dentro de um service, onde o service lida com a obtenção de dados e as entities não precisa sair de seu escopo. E é só isso. Uma situação provável. Isso é verdade do caso de você optar por usar DDD e optar por usar repositórios. Nesse caso e só nesse caso, é um processo que vale a pena ser seguido. É uma dica. Só isso.
Vejam que DDD envolve muito mais do que decidir best pratices de OO ou escolher os patterns. O principal do DDD é o entendimento do Domínio. Qualquer dificuldade na implementação deve ser averiguada junto ao domínio. Esse é o conceito de Refactoring Toward a Deeper Insight. Você refatorar seu código, baseado em um maior entendimento do domain.
Creio sim que há um problema pois há um pulo entre camadas. A camada do modelo não deve acessar diretamente o repositório, pois o repositório deve ser uma classe sem inteligência de negócio ou tratamento, o que deve ser feito na camada de negócio. Caso você insista em acessar o repositório, terá que por sua lógica dentro dele, e assim a lógica da sua aplicação estará distribuída em duas camadas, o que não é nada bom. Há um bom artigo da microsoft sobre esse padrão http://msdn.microsoft.com/en-us/library/ff649690.aspx
Sendo assim, o código poderia ser:
public class Cliente {
public ContasPagar[] listarContasPagar() {
ControladorContasPagar controladorContasPagar = // obtem o controlador de alguma forma...
return controladorContasPagar.listarPorCliente(this);
}
}
public class ControladorContasPagar {
public List<Cliente> listarPorCliente(Cliente cliente) {
RepositorioContasPagar repositorio = // obtem o repositorio
// codigo de tratamento
long codigo = // obtem o codigo
return repositorio.listarPorCodigo(codigo);
}
}