¿Es una mala práctica que el controlador llame al repositorio en lugar del servicio?

44

¿Es una mala práctica que el controlador llame al repositorio en lugar del servicio?

para explicar más:

Me doy cuenta de que en un buen diseño los controladores llaman al servicio y al repositorio de uso del servicio.

pero a veces en el controlador no tengo / necesito ninguna lógica y solo necesito obtener de db y pasarlo para ver.

y puedo hacerlo simplemente llamando al repositorio, no es necesario llamar al servicio, ¿es una mala práctica?

mohsenJsh
fuente
¿Cómo llamas al servicio? ¿A través de una interfaz REST?
Robert Harvey
2
Yo uso ese enfoque de diseño con bastante frecuencia. Mi controlador (o una clase de compositor subyacente) solicitará datos o enviará datos al repositorio, y luego los pasará a cualquier clase de servicio que necesite procesar. No hay razón para combinar las clases de procesamiento de datos con las clases de recuperación / gestión de datos, son preocupaciones diferentes, aunque sé que el enfoque típico es hacerlo de esa manera.
Jimmy Hoffa
3
Meh Si es una aplicación pequeña y solo está tratando de obtener datos de una base de datos, una capa de servicio es una pérdida de tiempo a menos que esa capa de servicio sea parte de alguna API pública, como una interfaz REST. "¿La leche es buena o mala para ti?" Depende de si eres intolerante a la lactosa.
Robert Harvey
44
No hay una regla estricta y rápida de que debería tener un Controlador -> Servicio -> Estructura de repositorio sobre Controlador -> Repositorio. Elija el patrón correcto para la aplicación correcta. Lo que diría es que debe hacer que su aplicación sea coherente.
NikolaiDante
Tal vez podría encontrar un servicio genérico que solo reenvíe su solicitud al repositorio y luego la devuelva. Esto podría ser útil para mantener una interfaz uniforme y sería simple si en el futuro necesita agregar un servicio real para hacer algo antes de llamar al repositorio.
Henrique Barcelos

Respuestas:

32

No, piénselo de esta manera: un repositorio es un servicio (también).

Si las entidades que recupera a través del repositorio manejan la mayor parte de la lógica de negocios, no hay necesidad de otros servicios. Solo tener el repositorio es suficiente.

Incluso si tiene algunos servicios que debe pasar para manipular sus entidades. Primero tome la entidad del repositorio y luego páselo a dicho servicio. Poder lanzar un HTTP 404 antes de intentarlo es muy conveniente.

También para los escenarios de lectura es común, solo necesita la entidad para proyectarlo en un DTO / ViewModel. Tener una capa de servicio intermedia a menudo resulta en una gran cantidad de métodos de transferencia que son bastante feos.

Joppe
fuente
2
¡Bien dicho! Prefiero llamar a repositorios, y solo en casos, entonces un repositorio no es suficiente (es decir, dos entidades deben modificarse usando repositorios diferentes), creo un servicio que es responsable de esta operación y lo llamo desde el controlador.
Zygimantas
Había notado un código bastante complicado solo para justificar el uso de un servicio. Absurdo, lo menos ...
Gi1ber7
Entonces, mi repositorio devuelve una lista de 'objetos comerciales' que necesito convertir en 'objetos xml', ¿es esa la razón suficiente para tener una capa de servicio? Estoy llamando a un método en cada objeto para convertirlo a otro tipo y agregarlo a una nueva lista.
bot_bot
El acceso directo a DAO es peligroso en los controladores, puede hacerte susceptible a las inyecciones de SQL y da acceso a acciones peligrosas como `` deleteAll ''. Definitivamente lo evitaría.
Anirudh
4

No es una mala práctica que un controlador llame a un repositorio directamente. Un "servicio" es solo otra herramienta, así que úselo donde tenga sentido.

NikolaiDante comentó:

... Elija el patrón correcto para la aplicación correcta. Lo que diría es que debe hacer que su aplicación sea coherente.

No creo que la consistencia sea el aspecto más importante. Una clase de "servicio" está destinada a encapsular alguna lógica de nivel superior para que el controlador no necesite implementarla. Si no se requiere una "lógica de nivel superior" para una operación determinada, simplemente vaya directamente al repositorio.

Para promover una buena Separación de preocupaciones y capacidad de prueba, el repositorio debe ser una dependencia que inyecte en el servicio a través de un constructor:

IFooRepository repository = new FooRepository();
FooService service = new FooService(repository);

service.DoSomething(...);

Si la búsqueda de registros en la base de datos necesita algún tipo de consulta parametrizada, una clase de servicio podría ser un buen lugar para tomar en su modelo de vista y construir una consulta que luego se ejecuta por el repositorio.

Del mismo modo, si tiene un modelo de vista complejo para un formulario, una clase de servicio puede encapsular la lógica de crear, actualizar y eliminar registros llamando a métodos en sus Modelos / Entidades de Dominio, y luego persistiéndolos usando un repositorio.

Yendo en la dirección opuesta, si su controlador necesita obtener un registro por su Id, delegar en un objeto de servicio para esto es como golpear una chincheta con un mazo: es mucho más de lo que necesita.

He descubierto que el controlador está en la mejor posición para manejar la transacción, o un objeto de Unidad de trabajo . El controlador o el objeto Unidad de trabajo luego delegaría en objetos de servicio para operaciones complejas, o iría directamente al repositorio para operaciones simples (como encontrar un registro por Id).

public class ShoppingCartsController : Controller
{
    [HttpPost]
    public ActionResult Edit(int id, ShoppingCartForm model)
    {
        // Controller initiates a database session and transaction
        using (IStoreContext store = new StoreContext())
        {
            // Controller goes directly to a repository to find a record by Id
            ShoppingCart cart = store.ShoppingCarts.Find(id);

            // Controller creates the service, and passes the repository and/or
            // the current transaction
            ShoppingCartService service = new ShoppingCartService(store.ShoppingCarts);

            if (cart == null)
                return HttpNotFound();

            if (ModelState.IsValid)
            {
                // Controller delegates to a service object to manipulate the
                // Domain Model (ShoppingCart)
                service.UpdateShoppingCart(model, cart);

                // Controller decides to commit changes
                store.SaveChanges();

                return RedirectToAction("Index", "Home");
            }
            else
            {
                return View(model);
            }
        }
    }
}

Creo que una combinación de servicios y trabajar con repositorios directamente es perfectamente aceptable. Puede encapsular aún más la transacción en un objeto de Unidad de trabajo si siente la necesidad.

El desglose de responsabilidades es el siguiente:

  • El controlador controla el flujo de la aplicación.
    • Devuelve "404 Not Found" si el carrito de compras no está en la base de datos
    • Vuelve a representar el formulario con mensajes de validación si la validación falla
    • Guarda el carrito de compras si todo sale
  • El controlador delega a una clase de servicio para ejecutar la lógica de negocios en sus Modelos de dominio (o Entidades). ¡Los objetos de servicio no deberían implementar la lógica empresarial! Ellos ejecutan la lógica de negocio.
  • Los controladores pueden delegar directamente en repositorios para operaciones simples
  • Los objetos de servicio toman datos en el modelo de vista y delegan en Modelos de dominio para ejecutar la lógica empresarial (por ejemplo, el objeto de servicio llama a métodos en los Modelos de dominio antes de llamar a métodos en el repositorio)
  • Los objetos de servicio delegan en repositorios para la persistencia de datos
  • Los controladores deberían:
    1. Administrar la vida útil de una transacción, o
    2. Crear un objeto de Unidad de trabajo para administrar la vida útil de una transacción
Greg Burghardt
fuente
1
-1 para poner DbContext en un controlador en lugar de un repositorio. El repositorio está destinado a administrar proveedores de datos para que nadie más tenga que hacerlo en caso de que un proveedor de datos cambie (de MySQL a archivos JSON planos, por ejemplo, cambie en un solo lugar)
Jimmy Hoffa
@JimmyHoffa: en realidad estoy mirando el código que he escrito y, para ser sincero, creo un objeto de "contexto" para mis repositorios, no necesariamente la base de datos. Creo que DbContextes un mal nombre en este caso. Voy a cambiar eso Uso NHibernate, y los repositorios (o contexto si es útil) administran el final de la base de datos, por lo que cambiar los mecanismos de persistencia no requiere cambios de código fuera del contexto.
Greg Burghardt
usted parece estar confundiendo el controlador con el repositorio por la apariencia de su código ... Quiero decir, su "contexto" está completamente equivocado y no debería existir absolutamente en el controlador.
Jimmy Hoffa
66
No tengo que responder y no estoy seguro de que esta sea una buena pregunta para empezar, pero rechazo el voto porque creo que su enfoque es un mal diseño. Sin resentimientos, solo desaliento que los contextos sean propiedad de los controladores. OMI, un controlador no debería estar iniciando y confirmando transacciones como esta. Ese es el trabajo de muchos otros lugares, prefiero que los controladores deleguen todo lo que no está simplemente cumpliendo la solicitud HTTP a otro lugar.
Jimmy Hoffa
1
Un repositorio, generalmente es responsable de toda la información del contexto de datos para garantizar que nada más necesite saber nada sobre las preocupaciones de datos además de lo que el dominio en sí necesita saber
Jimmy Hoffa
1

Depende de tu arquitectura. Yo uso Spring, y la transaccionalidad siempre es administrada por los servicios.

Si llama a los repositorios directamente para operaciones de escritura (o servicios simples sin lógica que simplemente delegan en el repositorio), probablemente esté utilizando varias transacciones de base de datos para una operación que debe realizarse en una. Eso conducirá a datos incoherentes en su base de datos. Como regla general, las operaciones de la base de datos deberían funcionar, o deberían fallar, pero las operaciones de medio trabajo son la causa de dolores de cabeza.

Por esa razón, creo que llamar a los repositorios directamente desde los controladores, o usar servicios de delegación simples, es una mala práctica. Empiezas a hacerlo solo para leer, y muy pronto tú, o uno o tus compañeros, comenzarán a hacerlo para operaciones de escritura.

Rober2D2
fuente