¿Diferencia entre repositorio y capa de servicio?


191

En los patrones de diseño de OOP, ¿cuál es la diferencia entre el patrón de repositorio 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á entendiendo ... ¡todavía!

Respuestas:


330

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...}     
             }
         );
     }
}

2
@Sam Striano: Como puede ver arriba, mi IRepository devuelve IQueryable. Esto permite agregar condiciones adicionales donde y ejecución diferida en la capa de servicio, no más tarde. Sí, uso un ensamblaje, pero todas estas clases se colocan en diferentes espacios de nombres. No hay razón para crear muchos ensamblajes en proyectos pequeños. La separación de nombres y carpetas funciona bien.
LukLed

81
¿Por qué devolver modelos de vista en el servicio? ¿no se supone que el servicio debe emular si tuviera varios clientes (móvil / web)? Si ese es el caso, entonces el modelo de vista puede diferir de las diferentes plataformas
Ryan

12
De acuerdo con @Ryan, la capa de servicio debería devolver un objeto de entidad o una colección de objetos de entidad (no IQueryable). Luego, en ui, la entidad se asigna a SomeViewModel por Automapper, por ejemplo.
Eldar

55
@Duffp: no es necesario crear un repositorio para cada entidad. Puede utilizar la aplicación genérica y se unen 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.
LukLed

2
en lugar de devolver viewmodel desde la capa de servicio, debe devolver DTO y convertirlo en viewModels usando automapper ... a veces son lo mismo, cuando no lo estén, agradecerá que haya implementado YGTNI "You're Going To Need It "
hanzolo

41

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.


Estoy de acuerdo contigo Mikael. De hecho, he aplicado el mismo escenario en mi blog de tecnología freecodebase.com y usé el primer enfoque de código en esta implementación. El código fuente se puede descargar aquí también.
Toffee

He estado investigando el tema general de aplicar un patrón de repositorio en una aplicación MVC existente. Es un marco a medida con un ORM similar a Active Record y otras convenciones de Rails / Laravel, y tiene algunos problemas arquitectónicos para el trabajo que estoy haciendo ahora. Una cosa que encontré es que los repositorios "no deberían devolver ViewModels, DTO u objetos de consulta", sino que deberían devolver objetos de repositorio. Estoy pensando en dónde los servicios interactúan con los objetos del repositorio a través de métodos como onBeforeBuildBrowseQueryy puedo usar el generador de consultas para alterar la consulta.
calligraphic-io

@Toffee, tu enlace está roto, ¿puedes actualizarlo? Necesito el código fuente para esta implementación.
Hamza Khanzada

24

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:

  1. Si desea usar estos servicios, es mejor que use MVVM y aquí está el ViewModel que necesita usar. ¡Ay!

  2. 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?

  3. 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).

  4. 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!


3
Solo tuve que comentar sobre esto, aunque sé que no se suma a la discusión: "Eso es solo un POCO con un mal nombre". << - ¡Eso se vería bien en una camiseta! :) :)
Mephisztoe

8

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.


Entonces, en una aplicación ASP.NET MVC que usa EF4, tal vez algo como esto: SQL Server -> EF4 -> Repository -> Service Layer -> Model -> Controller y viceversa.
Sam

1
Sí, su repositorio podría usarse para obtener entidades ligeras de EF4; y su capa de servicio podría usarse para enviarlos de regreso a un administrador de modelos especializado (Modelo en su escenario). El controlador llamará a su administrador de modelos especializado para hacer esto ... Eche un vistazo rápido a mi blog para Mvc 2 / 3. Tengo diagramas.
CarneyCode

Solo para aclarar: EF4 en su escenario es donde Modelo está en mis diagramas y Modelo en su escenario son gerentes de modelos especializados en mis diagramas
CarneyCode

5

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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.