Confuso sobre como utilizar corretamente um padrão de repositório com Camada de Serviço / Business no topo

5

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 .

    
por Bio2hazard 08.07.2014 / 03:12
fonte

1 resposta

1

Qual é o tamanho do seu aplicativo? Há uma boa chance de você estar pensando demais nisso. A menos que o aplicativo seja um aplicativo grande e de nível corporativo, o alto grau de acoplamento flexível que você está defendendo é provavelmente desnecessário.

Se você decidir que esse nível de acoplamento flexível ainda é necessário, crie uma camada de serviço que retorne objetos de "serviço". Isso preenche uma função semelhante à View Models no MVC. Em seguida, você gravará o código na camada de serviço que mapeia os objetos do modelo de domínio para o modelo de serviço.

Note que parte da sua luta pode ser devida ao fato de que, enquanto seu Repositório de Dados está retornando objetos CRUD, sua Camada de Serviço deve estar retornando o resultado das ações. Por exemplo:

public InvoiceView GetInvoice(int invoiceID);

retorna um objeto InvoiceView contendo todos os dados que você deseja expor ao público, incluindo Nome, Endereço, Itens de linha e assim por diante, de várias tabelas / objetos diferentes no seu domínio.

public class InvoiceView
{
    public int InvoiceID;
    public Address ShippingAddress;
    public Address BillingAddress;
    public List<LineItem> LineItems;
    ...
}

Da mesma forma, haverá métodos de camada de serviço que simplesmente executam uma ação e retornam um objeto indicando o resultado da ação:

public TransactionResult Transfer(
    int sourceAccountID, int targetAccountID, Money amount, ValidationToken token);

IMPORTANTE: Você nunca conseguirá se separar completamente dos seus dados. Seu consumidor sempre terá que ter algum conhecimento dos dados, mesmo que seja apenas um ID de objeto ou um UUID.

    
por 08.07.2014 / 03:57
fonte