Respostas:
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...}
}
);
}
}
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.
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.
onBeforeBuildBrowseQuery
e podem usar o construtor de consultas para alterar a consulta.
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:
Se você deseja usar esses serviços, é melhor usar o MVVM e aqui está o ViewModel que você precisa usar. Ai!
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?
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.)
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!
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.
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.