Diferença entre Repositório e Camada de Serviço?

188

Nos Padrões de Design do OOP, qual é a diferença entre o Padrão de Repositório e uma Camada de Serviço?

Estou trabalhando em um aplicativo ASP.NET MVC 3 e estou tentando entender esses padrões de design, mas meu cérebro não está conseguindo ... ainda!

Sam
fonte

Respostas:

327

Camada de repositório fornece um nível adicional de abstração sobre o acesso a dados. Em vez de escrever

var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();

para obter um único item do banco de dados, você usa a interface do repositório

public interface IRepository<T>
{
    IQueryable<T> List();
    bool Create(T item);
    bool Delete(int id);
    T Get(int id);
    bool SaveChanges();
}

e ligar Get(id). Camada de repositório expõe CRUD básico operações .

A camada de serviço expõe a lógica de negócios, que usa o repositório. Um serviço de exemplo pode parecer com:

public interface IUserService
{
    User GetByUserName(string userName);
    string GetUserNameByEmail(string email);
    bool EditBasicUserData(User user);
    User GetUserByID(int id);
    bool DeleteUser(int id);
    IQueryable<User> ListUsers();
    bool ChangePassword(string userName, string newPassword);
    bool SendPasswordReminder(string userName);
    bool RegisterNewUser(RegisterNewUserModel model);
}

Enquanto o List()método do repositório retorna todos os usuários, o ListUsers()IUserService pode retornar apenas os que o usuário tem acesso.

No ASP.NET MVC + EF + SQL SERVER, tenho esse fluxo de comunicação:

Exibições <- Controladores -> Camada de serviço -> Camada de repositório -> EF -> SQL Server

Camada de serviço -> Camada de repositório -> EF Esta parte opera em modelos.

Exibições <- Controladores -> Camada de serviço Esta parte opera em modelos de exibição.

EDITAR:

Exemplo de fluxo para / Orders / ByClient / 5 (queremos ver a ordem para um cliente específico):

public class OrderController
{
    private IOrderService _orderService;

    public OrderController(IOrderService orderService)
    {
        _orderService = orderService; // injected by IOC container
    }

    public ActionResult ByClient(int id)
    {
        var model = _orderService.GetByClient(id);
        return View(model); 
    }
}

Esta é a interface para o serviço de pedidos:

public interface IOrderService
{
    OrdersByClientViewModel GetByClient(int id);
}

Essa interface retorna o modelo de exibição:

public class OrdersByClientViewModel
{
     CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used
     IEnumerable<OrderViewModel> Orders { get; set; }
}

Esta é a implementação da interface. Ele usa classes de modelo e repositório para criar o modelo de visualização:

public class OrderService : IOrderService
{
     IRepository<Client> _clientRepository;
     public OrderService(IRepository<Client> clientRepository)
     {
         _clientRepository = clientRepository; //injected
     }

     public OrdersByClientViewModel GetByClient(int id)
     {
         return _clientRepository.Get(id).Select(c => 
             new OrdersByClientViewModel 
             {
                 Cient = new ClientViewModel { ...init with values from c...}
                 Orders = c.Orders.Select(o => new OrderViewModel { ...init with values from o...}     
             }
         );
     }
}
LukLed
fonte
2
@ Sam Striano: Como você pode ver acima, meu IRepository retorna IQueryable. Isso permite adicionar condições where adicionais e execução adiada na camada de serviço, o mais tardar. Sim, eu uso um assembly, mas todas essas classes são colocadas em diferentes namespaces. Não há razão para criar muitas montagens em pequenos projetos. A separação de namespace e pasta funciona bem.
LukLed
81
Por que retornar modelos de exibição no serviço? o serviço não deve emular se você tiver vários clientes (dispositivos móveis / web)? Se isso é o caso, então o viewmodel podem ser diferentes a partir de diferentes plataformas
Ryan
12
Concordado com @Ryan, a camada de serviço deve retornar um objeto de entidade ou uma coleção de objetos de entidade (não IQueryable). Em seguida, na entidade ui mapeia para SomeViewModel por Automapper, por exemplo.
Eldar
5
@ Duffp: Você não precisa criar repositórios para todas as entidades. Você pode usar a implementação genérica e vincular IRepository<>- se à GenericRepository<>sua biblioteca IOC. Esta resposta é muito antiga. Eu acho que a melhor solução é combinar todos os repositórios em uma classe chamada UnitOfWork. Ele deve conter repositório de todos os tipos e um método chamado SaveChanges. Todos os repositórios devem compartilhar um contexto EF.
LukLed
2
em vez de retornar o viewmodel da camada de serviço, você deve retornar o DTO e convertê-lo em viewModels usando o automapper .. às vezes são iguais, quando não são, você será grato por ter implementado o YGTNI "Você está indo para Need It "
hanzolo
41

Como Carnotaurus disse, o repositório é responsável por mapear seus dados do formato de armazenamento para seus objetos de negócios. Ele deve lidar com como ler e gravar dados (excluir, atualizar também) de e para o armazenamento.

O objetivo da camada de serviço, por outro lado, é encapsular a lógica de negócios em um único local para promover a reutilização de código e separações de preocupações. O que isso normalmente significa para mim na prática ao criar sites MVC do Asp.net é que eu tenho essa estrutura

[Controlador] chama [Serviço (s)] que chama [repositório (s)]

Um princípio que achei útil é manter a lógica no mínimo em controladores e repositórios.

Nos controladores, é porque ajuda a me manter SECO. É muito comum que eu precise usar a mesma filtragem ou lógica em outro lugar e se eu o coloquei no controlador, não posso reutilizá-lo.

Nos repositórios, é porque quero poder substituir meu armazenamento (ou ORM) quando algo melhor surgir. E se eu tiver lógica no repositório, preciso reescrevê-la quando alterar o repositório. Se meu repositório retornar apenas IQueryable e o serviço fizer a filtragem, por outro lado, só precisarei substituir os mapeamentos.

Por exemplo, substituí recentemente vários dos meus repositórios Linq-To-Sql pelo EF4 e aqueles onde eu permaneci fiel a esse princípio poderiam ser substituídos em questão de minutos. Onde eu tinha alguma lógica, era uma questão de horas.

Mikael Eliasson
fonte
Eu concordo com você Mikael. Na verdade, apliquei o mesmo cenário no meu blog técnico freecodebase.com e usei a primeira abordagem de código nesta implementação. O código-fonte também pode ser baixado aqui.
Toffee
Pesquisei o assunto geral da aplicação de um padrão de repositório em um aplicativo MVC existente. É uma estrutura sob medida com um ORM semelhante ao Active Record e outras convenções do Rails / Laravel, e tem alguns problemas de arquitetura para o trabalho que estou fazendo agora. Uma coisa que me deparei é que os repositórios "não devem retornar ViewModels, DTOs ou objetos de consulta", mas devem retornar objetos de repositório. Estou pensando em onde os serviços interagem com objetos de repositório através de métodos como onBeforeBuildBrowseQuerye podem usar o construtor de consultas para alterar a consulta.
io caligráfico
@ Coffee, seu link está quebrado, você pode atualizá-lo, preciso do código fonte para esta implementação.
Hamza Khanzada
24

A resposta aceita (e votada centenas de vezes) tem uma falha importante. Eu queria salientar isso no comentário, mas ele será enterrado lá embaixo em 30 comentários de algo tão destacado aqui.

Eu assumi um aplicativo corporativo que foi construído dessa maneira e minha reação inicial foi WTH ? ViewModels na camada de serviço? Eu não queria mudar a convenção porque anos de desenvolvimento haviam entrado nela, então continuei retornando os ViewModels. Rapaz, isso se transformou em um pesadelo quando começamos a usar o WPF. Nós (a equipe de desenvolvedores) estávamos sempre dizendo: qual ViewModel? O real (o que escrevemos para o WPF) ou o de serviços? Eles foram escritos para um aplicativo Web e tinham o sinalizador IsReadOnly para desativar a edição na interface do usuário. Major, grande falha e tudo por causa de uma palavra: ViewModel !!

Antes de cometer o mesmo erro, eis mais algumas razões além da minha história acima:

Retornar um ViewModel da camada de serviço é um grande não, não. É como dizer:

  1. Se você deseja usar esses serviços, é melhor usar o MVVM e aqui está o ViewModel que você precisa usar. Ai!

  2. Os serviços estão assumindo que eles serão exibidos em uma interface do usuário em algum lugar. E se for usado por um aplicativo que não seja da interface do usuário, como serviços da Web ou serviços do Windows?

  3. Esse nem é um ViewModel real. Um ViewModel real tem observabilidade, comandos etc. Isso é apenas um POCO com um nome ruim. (Veja minha história acima para saber por que os nomes são importantes.)

  4. O aplicativo consumidor é melhor uma camada de apresentação (os ViewModels são usados ​​por essa camada) e ele entende melhor C #. Outro Ai!

Por favor, não faça isso!

CodificaçãoYoshi
fonte
3
Eu apenas tive que comentar sobre isso, embora saiba que isso não contribui para a discussão: "Isso é apenas um POCO com um nome ruim". << - Ficaria legal em uma camiseta! :) :)
Mephisztoe 01/03/19
8

Geralmente, um repositório é usado como andaime para preencher suas entidades - uma camada de serviço sai e origina uma solicitação. É provável que você coloque um repositório na sua camada de serviço.

CarneyCode
fonte
Assim, em um aplicativo ASP.NET MVC usando EF4, talvez algo como isto: SQL Server -> EF4 -> Repositório -> Camada de Serviço -> Modelo -> Controlador e vice-versa?
Sam
1
Sim, seu repositório pode ser usado para obter entidades leves do EF4; e sua camada de serviço pode ser usada para enviá-las de volta a um gerente de modelo especializado (modelo no seu cenário). O controlador chamaria seu gerente de modelo especializado para fazer isso ... Dê uma olhada rápida no meu blog para o Mvc 2 / 3. Eu tenho diagramas.
CarneyCode 19/02/11
Só para esclarecer: EF4 em seu cenário é onde Modelo é em meus diagramas e Modelo em seu cenário são especializados gerentes modelo em meus diagramas
CarneyCode
4

A camada de repositório é implementada para acessar o banco de dados e ajuda a estender as operações CRUD no banco de dados. Enquanto uma camada de serviço consiste na lógica de negócios do aplicativo e pode usar a camada de repositório para implementar certa lógica que envolve o banco de dados. Em um aplicativo, é melhor ter uma camada de repositório e uma camada de serviço separadas. Ter camadas separadas de repositório e serviço torna o código mais modular e separa o banco de dados da lógica de negócios.

Akshay
fonte