Respuestas:
La capa de repositorio le brinda un nivel adicional de abstracción sobre el acceso a datos. En lugar de escribir
var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();
para obtener un solo elemento de la base de datos, utiliza la interfaz del repositorio
public interface IRepository<T>
{
IQueryable<T> List();
bool Create(T item);
bool Delete(int id);
T Get(int id);
bool SaveChanges();
}
y llama Get(id)
. La capa de repositorio expone las operaciones CRUD básicas .
La capa de servicio expone la lógica empresarial, que utiliza el repositorio. El servicio de ejemplo podría verse así:
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);
}
Si bien el List()
método de repositorio devuelve todos los usuarios, ListUsers()
IUserService solo puede devolver algunos, a los que el usuario tiene acceso.
En ASP.NET MVC + EF + SQL SERVER, tengo este flujo de comunicación:
Vistas <- Controladores -> Capa de servicio -> Capa de repositorio -> EF -> SQL Server
Capa de servicio -> Capa de repositorio -> EF Esta parte opera en modelos.
Vistas <- Controladores -> Capa de servicio Esta parte funciona en modelos de vista.
EDITAR:
Ejemplo de flujo para / Orders / ByClient / 5 (queremos ver el pedido de un 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 es la interfaz para el servicio de pedidos:
public interface IOrderService
{
OrdersByClientViewModel GetByClient(int id);
}
Esta interfaz devuelve el modelo de vista:
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 es la implementación de la interfaz. Utiliza clases de modelo y repositorio para crear el modelo de vista:
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<>
a GenericRepository<>
de la biblioteca del COI. Esta respuesta es muy antigua. Creo que la mejor solución es combinar todos los repositorios en una clase llamada UnitOfWork
. Debe contener un repositorio de cada tipo y un método llamado SaveChanges
. Todos los repositorios deben compartir un contexto EF.
Como Carnotaurus dijo que el repositorio es responsable de asignar sus datos desde el formato de almacenamiento a sus objetos comerciales. Debería manejar cómo leer y escribir datos (eliminar, actualizar también) desde y hacia el almacenamiento.
El propósito de la capa de servicio, por otro lado, es encapsular la lógica de negocios en un solo lugar para promover la reutilización del código y la separación de preocupaciones. Lo que esto generalmente significa para mí en la práctica cuando construyo sitios MVC de Asp.net es que tengo esta estructura
[Controlador] llama [Servicio (s)] que llama [repositorio (s)]
Un principio que he encontrado útil es mantener la lógica al mínimo en los controladores y repositorios.
En los controladores es porque me ayuda a mantenerme SECO. Es muy común que necesite usar el mismo filtro o lógica en otro lugar y si lo coloco en el controlador no puedo reutilizarlo.
En los repositorios es porque quiero poder reemplazar mi almacenamiento (u ORM) cuando algo mejor aparece. Y si tengo lógica en el repositorio, necesito reescribir esta lógica cuando cambio el repositorio. Si mi repositorio solo devuelve IQueryable y el servicio realiza el filtrado, por otro lado, solo tendré que reemplazar las asignaciones.
Por ejemplo, recientemente reemplacé varios de mis repositorios Linq-To-Sql con EF4 y aquellos en los que me mantuve fiel a este principio podrían reemplazarlos en cuestión de minutos. Cuando tenía algo de lógica, era cuestión de horas.
onBeforeBuildBrowseQuery
y puedo usar el generador de consultas para alterar la consulta.
La respuesta aceptada (y votada cientos de veces) tiene una falla importante. Quería señalar esto en el comentario, pero solo quedará enterrado allí en 30 comentarios y así señalarlo aquí.
Me hice cargo de una aplicación empresarial que se creó de esa manera y mi reacción inicial fue WTH ? ViewModels en la capa de servicio? No quería cambiar la convención porque habían pasado años de desarrollo, así que seguí devolviendo ViewModels. Chico, se convirtió en una pesadilla cuando comenzamos a usar WPF. Nosotros (el equipo de desarrolladores) siempre decíamos: ¿qué ViewModel? ¿El real (el que escribimos para WPF) o el de servicios? Fueron escritos para una aplicación web e incluso tenían el indicador IsReadOnly para deshabilitar la edición en la interfaz de usuario. Mayor, mayor falla y todo por una sola palabra: ¡ ViewModel !
Antes de cometer el mismo error, aquí hay algunas razones más además de mi historia anterior:
Devolver un ViewModel desde la capa de servicio es un gran no no. Eso es como decir:
Si desea usar estos servicios, es mejor que use MVVM y aquí está el ViewModel que necesita usar. ¡Ay!
Los servicios suponen que se mostrarán en una interfaz de usuario en alguna parte. ¿Qué sucede si es utilizado por una aplicación que no es UI, como servicios web o servicios de Windows?
Eso ni siquiera es un ViewModel real. Un ViewModel real tiene observabilidad, comandos, etc. Eso es solo un POCO con un mal nombre. (Vea mi historia anterior para saber por qué los nombres son importantes).
Es mejor que la aplicación de consumo sea una capa de presentación (esta capa usa ViewModels) y comprende mejor C #. ¡Otro ay!
Por favor, no hagas eso!
Por lo general, un repositorio se utiliza como andamiaje para poblar sus entidades; una capa de servicio saldría y generaría una solicitud. Es probable que coloque un repositorio debajo de su capa de servicio.
La capa de repositorio se implementa para acceder a la base de datos y ayuda a extender las operaciones CRUD en la base de datos. Mientras que una capa de servicio consiste en la lógica de negocios de la aplicación y puede usar la capa de repositorio para implementar cierta lógica que involucra la base de datos. En una aplicación, es mejor tener una capa de repositorio y una capa de servicio separadas. Tener un repositorio separado y capas de servicio hace que el código sea más modular y desacople la base de datos de la lógica empresarial.