Estou construindo uma solução ASP.NET Web Api 2 para fins de aprendizado, e acertei um obstáculo. Eu queria saber se alguém poderia me dizer exatamente o que é que eu estou sentindo falta.
Minha solução da Web Api tem 4 camadas:
- Dados, que contêm os modelos de dados (POCOs), mapeamentos de APIs fluentes, contexto EF, migrações e propagação
- Repositório, que implementa o CRUD para todos os modelos de dados e retorna os modelos de negócios
- Domínio / Serviço / Empresa que fornece a interface IRepository e deve conter serviços que implementam a lógica de negócios do aplicativo. Também contém os modelos de negócios.
- Web, os controladores Web Api e DI / IoC para unir tudo.
As referências são as seguintes: Dados < Repositório > Domínio
Web faz referência a todas as 3 camadas para DI.
A ideia por trás da camada de domínio separada é que ela é completamente alheia a qualquer coisa que ocorra na camada de Dados ou Repositório. Ele simplesmente descreve seus requisitos fornecendo o repositório de IR para a camada Repositório e os Modelos de Negócios que deseja obter como resultado. Cabe à camada Repository atender às solicitações da camada de domínio.
Aqui está meu problema :
- Como o Domínio não possui vínculos com Dados, ele não sabe nada sobre a estrutura do modelo de Dados. Isso significa que a camada de repositório precisa converter tudo para o Modelo de Negócios antes de retorná-lo. - Suponho que eu poderia retorná-lo como um objeto genérico e, em seguida, mapeie o objeto genérico para o modelo de negócios dentro da camada de domínio. Isso seria mais apropriado?
- Novamente, como o Domínio não sabe nada sobre o Modelo de Dados, não posso passar nenhuma expressão para aplicar no conjunto de dados ao meu Repositório. Isso significa que para cada propriedade que eu quero para WHERE, eu teria que criar um repositório específico de POCO com IBussinessModelRepository correspondente que delineie seus requisitos e, em seguida, implemente esse requisito. Isso rapidamente se torna tedioso, já que exige um monte de "GetByProp1", "GetByProp2" - código central do macaco.
- E se eu quiser criar um modelo de negócios composto que exija uma consulta complexa para hidratar? Minha única opção é criar ainda outro IBusinessRepository com um Repositório correspondente e, em seguida, gravar a consulta complexa na camada de repositório? Se for esse o caso, parece que a camada de serviço é, na maioria das vezes, apenas mais uma etapa que transmite solicitações não essenciais para repositórios específicos.
-
É apropriado para os controladores da API da Web enviar solicitações diretamente para o repositório? Parece muito bobo rotear todas as solicitações CRUD através da camada de serviço, que apenas passa a solicitação para a camada de repositório de qualquer maneira. Não, não é como a camada de serviço deve ser o ponto de entrada para todas as operações. (Com a Web apenas agindo como um consumidor dos serviços oferecidos.)
- Atualmente, não uso uma unidade de trabalho porque há muitas opiniões conflitantes quando se trata de UoW + EF. Ainda não tenho experiência suficiente para estar em ambos os lados da cerca. Um UoW seria útil nesse cenário?
Sinto que estou perdendo uma parte vital no que faz essa abordagem de padrão / camada funcionar.
Eu sei que eu poderia facilmente continuar trapaceando (ou seja, dar acesso ao serviço para dados específicos, como o contexto), mas estou mais interessado em aprender como fazer isso da maneira correta.
A pesquisa que fiz até agora produziu resultados que variam muito:
- Do Repositório usando o AutoMapper para alterar o tipo para o modelo de negócios sem realmente executar a consulta, permitindo que a camada de serviço passe Expressões de modelo de negócios que podem ser aplicadas e executando a consulta, retornando o resultado mapeado - parece ótimo , exceto que essa abordagem parece não funcionar para mim, já que o AutoMapper não consegue lidar com meus modelos com propriedades de navegação recursivas.
- Para a camada de serviço, fazendo interface direta com os POCOs. (E, assim, ter acesso direto ao DAL)
O que estou perdendo?
Edite para explicar mais sobre o meu código:
Meu Repositório Genérico no Github contém métodos CRUD como este:
public abstract class RepositoryBase<TData, TDomain, TId> : IRepository<TDomain, TId> where TDomain : EntityBase<TId>, new() where TData : class, IDataEntity<TId>, new()
{
private readonly ShortStuffContext _context;
public RepositoryBase(ShortStuffContext context)
{
_context = context;
}
public IEnumerable<TDomain> GetAll()
{
return _context.Set<TData>().BuildQuery<TData, TDomain>();
}
}
O BuildQuery é uma Extensão do LINQ que captura todas as entidades ou pode usar uma expressão para buscar uma única entidade e, em seguida, usa ValueInjecter para mapear o Modelo de Dados para o Modelo de Domínio.
Os métodos de repositório são solicitados pela camada de domínio via interface:
public interface IRepository<TDomain, TId> where TDomain : EntityBase<TId>
{
IEnumerable<TDomain> GetAll();
}
O RepositoryBase é estendido por Repositórios para os POCOs individuais, que atualmente estão vazios, fornecendo apenas TData, TDomain e TId para a Base de Repositório:
public class UserRepository : RepositoryBase<Data.Entities.User, User, decimal>
{
public UserRepository(ShortStuffContext context)
: base(context)
{
}
}
O qual pode ser solicitado pela camada de domínio por meio da injeção de construtor IRepository<User, decimal> UserRepository
por meio de uma ligação ninject InRequestScope
.