Pelo que entendi, no DDD, é apropriado usar um padrão de repositório com uma raiz agregada. Minha pergunta é: devo retornar os dados como uma entidade ou objetos de domínio / DTO?
Talvez algum código explique mais a minha pergunta:
Entidade
public class Customer
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Devo fazer algo assim?
public Customer GetCustomerByName(string name) { /*some code*/ }
Ou algo parecido com isto?
public class CustomerDTO
{
public Guid Id { get; set; }
public FullName { get; set; }
}
public CustomerDTO GetCustomerByName(string name) { /*some code*/ }
Pergunta adicional:
- Em um repositório, devo retornar IQueryable ou IEnumerable?
- Em um serviço ou repositório, eu deveria fazer algo como ..
GetCustomerByLastName
,GetCustomerByFirstName
,GetCustomerByEmail
? ou apenas criar um método que seja algo parecidoGetCustomerBy(Func<string, bool> predicate)
?
c#
domain-driven-design
repository-pattern
codefish
fonte
fonte
GetCustomerByName('John Smith')
retornará se você tiver vinte John Smiths em seu banco de dados? Parece que você está assumindo que não há duas pessoas com o mesmo nome.Respostas:
Bem, isso depende inteiramente dos seus casos de uso. A única razão pela qual consigo pensar em retornar um DTO em vez de uma entidade completa é se sua entidade é enorme e você só precisa trabalhar em um subconjunto dele.
Se for esse o caso, talvez você deva reconsiderar seu modelo de domínio e dividir sua grande entidade em entidades menores relacionadas.
Uma boa regra geral é sempre retornar o tipo mais simples (mais alto na hierarquia de herança) possível. Portanto, retorne, a
IEnumerable
menos que você queira deixar o consumidor do repositório trabalhar com umIQueryable
.Pessoalmente, acho que retornar um
IQueryable
é uma abstração com vazamento, mas conheci vários desenvolvedores que argumentam apaixonadamente que não é. Na minha opinião, toda lógica de consulta deve ser contida e ocultada pelo Repositório. Se você permitir que o código de chamada personalize sua consulta, qual é o sentido do repositório?Pela mesma razão que eu mencionei no ponto 1, definitivamente não use
GetCustomerBy(Func<string, bool> predicate)
. Pode parecer tentador no começo, mas é exatamente por isso que as pessoas aprenderam a odiar repositórios genéricos. Está com vazamento.Coisas como
GetByPredicate(Func<T, bool> predicate)
só são úteis quando estão escondidas atrás de aulas concretas. Portanto, se você tivesse uma classe base abstrata chamadaRepositoryBase<T>
cuja exposiçãoprotected T GetByPredicate(Func<T, bool> predicate)
era usada apenas por repositórios concretos (por exemplo,public class CustomerRepository : RepositoryBase<Customer>
), isso seria bom.fonte
Há uma comunidade considerável de pessoas que usam o CQRS para implementar seus domínios. Meu sentimento é que, se a interface do seu repositório for análoga às melhores práticas usadas por eles, você não se extrapolará.
Com base no que eu vi ...
1) Os manipuladores de comando geralmente usam o repositório para carregar o agregado por meio de um repositório. Os comandos visam uma única instância específica do agregado; o repositório carrega a raiz por ID. Não há, pelo que vejo, um caso em que os comandos sejam executados em uma coleção de agregados (em vez disso, você executaria uma consulta para obter a coleção de agregados, depois enumerar a coleção e emitir um comando para cada um.
Portanto, em contextos em que você modificará o agregado, eu esperaria que o repositório retornasse a entidade (também conhecida como raiz agregada).
2) Os manipuladores de consulta não tocam nos agregados; em vez disso, eles trabalham com projeções dos objetos de valor agregado que descrevem o estado dos agregados / agregados em algum momento no tempo. Então, pense em ProjectionDTO, em vez de AggregateDTO, e você tem a idéia certa.
Nos contextos em que você executará consultas no agregado, prepará-lo para exibição e assim por diante, eu esperaria ver um DTO, ou uma coleção de DTO, retornada, em vez de uma entidade.
Todas as suas
getCustomerByProperty
chamadas parecem consultas para mim, para que se enquadram na última categoria. Eu provavelmente gostaria de usar um único ponto de entrada para gerar a coleção, portanto, estaria olhando para ver seé uma escolha razoável; os manipuladores de consulta construiriam a especificação apropriada a partir dos parâmetros fornecidos e passariam essa especificação para o repositório. A desvantagem é que a assinatura realmente sugere que o repositório é uma coleção na memória; não está claro para mim que o predicado o comprará muito se o repositório for apenas uma abstração da execução de uma instrução SQL em um banco de dados relacional.
Existem alguns padrões que podem ajudar, no entanto. Por exemplo, em vez de construir a especificação manualmente, passe ao repositório uma descrição das restrições e permita que a implementação do repositório decida o que fazer.
Aviso: java como digitação detectada
Concluindo: a escolha entre fornecer um agregado e fornecer um DTO depende do que você espera que o consumidor faça com ele. Meu palpite seria uma implementação concreta suportando uma interface para cada contexto.
fonte
getCustomersThatSatisfy(Specification spec)
listarei as propriedades necessárias para as opções de pesquisa. Estou acertando?Com base no meu conhecimento de DDD, você deve ter isso,
Depende, você encontrará visões mistas de diferentes povos para esta pergunta. Mas, pessoalmente, acredito que, se o seu repositório for usado por um serviço como CustomerService, você poderá usar o IQueryable, caso contrário, permanecerá no IEnumerable.
Repositórios devem retornar IQueryable?
No seu repositório, você deve ter uma função genérica, como você disse,
GetCustomer(Func predicate)
mas na sua camada de serviço, adicione três métodos diferentes, porque há uma chance de seu serviço ser chamado de clientes diferentes e eles precisarem de DTOs diferentes.Você também pode usar o Generic Repository Pattern para CRUD comum, se ainda não estiver ciente.
fonte