asp.net mvc - ventajas - Diferencia entre el repositorio y la capa de servicio?
modelo vista controlador ventajas y desventajas (4)
En OOP Design Patterns, ¿cuál es la diferencia entre el patrón de depósito y una capa de servicio?
Estoy trabajando en una aplicación ASP.NET MVC 3, y estoy tratando de entender estos patrones de diseño, pero mi cerebro simplemente no lo está ... ¡todavía!
Como dijo Carnotaurus, el repositorio es responsable de mapear sus datos desde el formato de almacenamiento hasta sus objetos comerciales. Debe manejar tanto cómo leer y escribir datos (eliminar, actualizar también) desde y hacia el almacenamiento.
El objetivo de la capa de servicio, por otro lado, es encapsular la lógica empresarial en un solo lugar para promover la reutilización del código y la separación de las preocupaciones. Lo que esto normalmente significa para mí en la práctica al construir Asp.net MVC sitios es que tengo esta estructura
[Controlador] llama [Servicio (s)] que llama [repositorio (es)]
Un principio que he encontrado útil es mantener la lógica al mínimo en controladores y repositorios.
En los controladores es porque ayuda a mantenerme SECO. Es muy común que necesite usar el mismo filtro o lógica en otro lugar y si lo coloqué en el controlador no puedo reutilizarlo.
En los repositorios es porque quiero poder reemplazar mi almacenamiento (o ORM) cuando algo mejor se presenta. Y si tengo lógica en el repositorio, necesito reescribir esta lógica cuando cambie el repositorio. Si mi repositorio solo devuelve IQueryable y el servicio filtra 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 reemplazarse en cuestión de minutos. Donde tenía algo de lógica era cuestión de horas.
La Capa de depósito le proporciona un nivel adicional de abstracción sobre el acceso a los 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 llame a Get(id)
. La capa de repositorio expone las operaciones CRUD básicas.
La capa de servicio expone la lógica empresarial, que usa 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);
}
Mientras que el método de repositorio List()
devuelve todos los usuarios, ListUsers()
de IUserService podría devolver solo los que el usuario tenga acceso.
En ASP.NET MVC + EF + SQL SERVER, tengo este flujo de comunicación:
Vistas <- Controladores -> Capa de servicio -> Capa de repositorio -> EF -> Servidor SQL
Capa de servicio -> Capa de repositorio -> EF Esta parte opera en modelos.
Vistas <- Controladores -> Capa de servicio Esta parte opera en modelos de vista.
EDITAR:
Ejemplo de flujo para / Orders / ByClient / 5 (queremos ver el orden para 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...}
}
);
}
}
La respuesta aceptada (y los cientos de veces votadas) tiene un defecto importante. Quería señalar esto en el comentario, pero lo enterrarán allí en 30 comentarios, por lo que lo señalaré aquí.
Me hice cargo de una aplicación empresarial que se construyó de esa manera y mi reacción inicial fue WTH ? ViewModels en la capa de servicio? No quería cambiar la convención debido a los años de desarrollo, así que continué con la devolución de ViewModels. Chico se convirtió en una pesadilla cuando comenzamos a usar WPF. Nosotros (el equipo de desarrolladores) siempre estábamos diciendo: ¿qué ViewModel? ¿El real (el que escribimos para el 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, gran falla y todo por una 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 utilizar estos servicios, será mejor que use MVVM y aquí está el ViewModel que necesita usar. ¡Ay!
Los servicios están asumiendo que se mostrarán en una IU 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 verdadero ViewModel. Un verdadero ViewModel tiene observabilidad, comandos, etc. Eso es solo un POCO con un mal nombre. (Vea mi historia anterior para saber por qué los nombres importan).
La aplicación consumidora será mejor una capa de presentación (la capa utiliza ViewModels) y comprenderá mejor C #. Otro Ouch!
Por favor, no hagas eso!
Por lo general, un repositorio se utiliza como andamio para poblar sus entidades: una capa de servicio se cerraría y generaría una solicitud. Es probable que coloque un repositorio debajo de su capa de servicio.