MVC: ¿Dónde poner la lógica empresarial? [cerrado]

81

En primer lugar, he visto muchas preguntas sobre esto, pero no hay suficientes razones detrás de eso. Si mi pregunta no es lo suficientemente buena y debe eliminarse, lo entenderé.

He echado un vistazo, por ejemplo, a esto y una respuesta votada a más de 45 dice que le aconseja que coloque la lógica empresarial en el modelo, lo que suena bastante lógico.

Sin embargo, mi primer proyecto grande lo hice con todo mi BL completamente en los controladores, porque no cuestioné estas cosas y miré cómo se hace en el AccountControllerque se agrega automáticamente si elige MVC con autenticación de formulario. Todos los métodos se ven bastante llenos de BL. ¿O tal vez es la menor cantidad de código que se pudo agregar y estoy pasando por alto algunas cosas?

Una persona en youtube me preguntó si tenía razón al poner toda la lógica en sus modelos y al principio yo estaba ¡no! ¡Entonces comencé a pensar que tal vez él tenía razón !?

Entonces, después de todo, ¿dónde pongo mi lógica empresarial? Si está en clases de modelos, entonces, ¿cuánto código debe considerarse una cantidad saludable en un método que está en el controlador? ¿Una línea para llamar a algún método del modelo en un controlador como máximo y luego volver a la vista?

Andrius Naruševičius
fuente
1
La lógica empresarial entra en los controladores. La lógica del modelo va en el modelo. La lógica del modelo son las cosas que tratan con el modelo específicamente / solo. Setters / getters / propiedades / sumadores / removedores, etc.
crush
1
@crush: No estoy de acuerdo. Como he leído: "Un objeto modelo contiene los datos de la aplicación y la" lógica empresarial "y" Los objetos del controlador vinculan el modelo y los objetos de vista ".
ChiefTwoPencils
@BobbyDigital: ¿puede proporcionar un enlace a la fuente? :)
Andrius Naruševičius
Claro, está en la explicación del uso adecuado de MVC en The Big Nerd Ranch Guide, pero desafortunadamente tendrás que comprar el libro para confirmarlo.
ChiefTwoPencils
Sin embargo, diré que, habiendo intentado confirmar esto en un libro de C #, en asp.net la lógica de negocios va en el nivel medio donde el nivel superior sería la interfaz de usuario, el medio sería el controlador y el inferior sería la base de datos. Pero no están hablando de manera específica / explícita sobre MVC. Esto viene de C # para programadores :)
ChiefTwoPencils

Respuestas:

54

Prefiero poner lógica de dominio en el modelo por un par de razones.

  1. El modelo no debe tener código de interfaz de usuario y, por lo tanto, debe ser más fácil de probar. Siempre que sea posible, me gusta tener un modelo que funcione completamente (es decir, una cobertura de prueba completa) antes de escribir cualquier código de IU. El controlador puede confiar en que el modelo está haciendo lo correcto y solo ocuparse de las preocupaciones de la interfaz de usuario.

  2. Si coloca la lógica de dominio en un controlador, no es tan fácil de compartir entre diferentes aplicaciones, o incluso entre diferentes controladores.

Ferruccio
fuente
2
Sí, me gustó mucho #2porque me resultó difícil compartir entre controladores (tuve que usar métodos como estáticos).
Andrius Naruševičius
Sí @ AndriusNaruševičius, no hagas eso. Debe intentar inyectar sus dependencias en los controladores y no depender de otros controladores.
Mark Walsh
Tenga en cuenta que creo que esta respuesta habla de "modelos de dominio" (parte M del patrón clásico MVC) que no está relacionado con el "modelo" ASP.Net MVC (parte del patrón MVVM).
Alexei Levenkov
1
Entonces ... ¿dónde pones la lógica que se basa en múltiples modelos?
Cañón de Kolob
1
@Ferrucio Esa es una mala solución. Terminas en un "infierno de dependencia" donde creas objetos que dependen de otros objetos para ser creados. Es una mala solución porque los datos innecesarios / no utilizados se pasan sin ningún motivo. Desea que las funciones tomen solo lo que necesitan para hacer el trabajo; de lo contrario, el código se vuelve poco claro muy rápidamente. La mejor solución es crear clases de lógica empresarial que requieran lo mínimo para construir (o mejor aún, usar la inyección de dependencia para construir). De esa manera, nunca tendrá que buscar varios objetos no relacionados para hacer la lógica comercial.
Cañón de Kolob
43

Me gusta mantener limpios mis modelos, es decir, solo con propiedades y sin lógica empresarial. Siempre creo que es bueno inyectar dependencias en el controlador y estas dependencias contienen la lógica que realizo en mis modelos. Me gusta adherirme al principio de responsabilidad única siempre que sea posible y encuentro que los modelos con toneladas de métodos se hinchan muy rápidamente. Hay ventajas y desventajas para ambos, inyectar muchas dependencias tiene una sobrecarga pero permite probar de forma aislada y mantiene las clases simples y terminará teniendo controladores más eficientes. A pesar de que mi lógica no existe realmente en mi modelo como miembros de la clase, sigue siendo lógica empresarial. Tiendo a no tener la lógica de negocios definida en el controlador, ya que burlarse de cosas como Httpcontext es un poco una pesadilla e innecesaria.

Mark Walsh
fuente
5
+1 de acuerdo completamente. ¡Pensé que era el único al que le gustaba mantener las responsabilidades de los modelos al mínimo!
Lee
1
Claro, él es una esencia para mi controlador de inicio de
Mark Walsh
1
Entonces, si no pones tu lógica en tus modelos o controladores, ¿dónde la pones?
niico
1
¿Cómo se pasan argumentos al constructor del controlador? ¿No se suele realizar la inicialización del controlador entre bastidores?
Jeff
1
Yo uso la inyección de dependencia.
Mark Walsh
23

La lógica de negocios pertenece al dominio del problema y todo lo que pertenece al dominio del problema va al modelo en MVC.

El controlador debe ser responsable de pasar los datos del modelo a la vista y de la vista al modelo. Por lo tanto, el controlador es el puente entre lo que interactúa el usuario y cómo el programa modela y almacena el estado del problema. La fontanería , por así decirlo.

La clave aquí es la distinción entre la lógica empresarial y la lógica de plomería. En mi opinión, lo que hace el controlador de cuenta generado automáticamente es principalmente plomería, no realmente lógica comercial. Tenga en cuenta que la lógica de la plomería no es necesariamente corta en absoluto, por lo que no es necesario imponer límites artificiales (como "X número de llamadas como máximo en el controlador").

Theodoros Chatzigiannakis
fuente
Estoy de acuerdo con todo esto. Sin embargo, creo que mucha confusión proviene de cómo estructurar las clases en el modelo, especialmente con EF. IE: ¿usas clases parciales y construyes la lógica en diferentes archivos C #? un archivo para EF y un archivo para la lógica?
S1r-Lanzelot
13

Mi equipo cuando se trasladó a mvc desde webforms (asp.net) investigó mucho y se le ocurrió la siguiente estructura. Según yo, no se trata de cuán grande o pequeña sea la aplicación. Se trata de mantener el código limpio y claro.

Proyecto DAL

AccountsDAL.cs --- > Calls SP or any ORM if ur using any

BLLProyecto

AccountsBLL.cs ---> Calls DAL

Proyecto Web

Model
    AccountsModel --- > Contains properties And call BLL
Controllers
    IndexController ---> Calls Models and returns View
Views
    Index

Los controladores deben ser responsables del paso de datos entre el modelo y la vista. Aparte de eso, no debería haber ningún código innecesario. Por ejemplo, si está registrando, debe hacerlo a nivel de modelo en lugar de controlador.

Muneeb Zulfiqar
fuente
13

Parece haber cierta confusión en torno a este tema. En general, parece que las personas tienden a confundir el patrón MVC con la arquitectura de N niveles como una situación de una u otra. La realidad es que los dos enfoques se pueden utilizar juntos, pero uno no depende del otro y no se requiere ninguno.

La arquitectura de N niveles se ocupa de separar una aplicación en varios niveles. Un ejemplo simple es donde la aplicación se divide en una capa de presentación, una capa de lógica empresarial y una capa de acceso a datos.

MVC es un patrón de diseño que se ocupa de la capa de presentación de una aplicación. Es totalmente posible diseñar una aplicación siguiendo un enfoque MVC sin separar la lógica de negocios y la lógica de acceso a datos de la capa de presentación y, por lo tanto, terminar con un diseño de un solo nivel.

El resultado, si está siguiendo un enfoque MVC sin separar también la aplicación en niveles, es que terminará con Modelos, Vistas y Controladores que tienen partes de reglas comerciales y lógica de acceso a datos mezcladas con el resto de la lógica.

Por definición, en una arquitectura de N niveles, se supone que el nivel de presentación solo puede comunicarse con la capa de lógica empresarial, por lo que debe mantener que cualquiera de los componentes MVC solo puede comunicarse con la capa de lógica empresarial.

Si está creando una aplicación que no incluye presentación y, por lo tanto, no es una capa de presentación, no debería tener que preocuparse por el patrón MVC. Sin embargo, es muy posible que aún pueda dividir su aplicación en varios niveles y, por lo tanto, seguir un diseño de N niveles aunque no haya una capa de presentación involucrada.

treefiddy
fuente
8

En términos generales, la lógica empresarial no debería residir en ninguno de los jugadores MVC; solo debe ser consumido por las acciones de su controlador.

Como muchos han mencionado, es mejor crear una biblioteca para alojar la lógica empresarial como un conjunto de componentes reutilizables independientes del cliente.

Cuando se hace de esta manera, aumentamos enormemente la capacidad de reutilización, compatibilidad, escalabilidad y capacidad de prueba con nuestro software. También reducimos nuestra dependencia de ciertas características del marco, lo que facilita la migración a tecnologías más nuevas / diferentes.

Resumir nuestra lógica empresarial en un ensamblaje (o ensamblajes) independiente nos ha sido de gran utilidad a lo largo de los años. Nuestra lógica empresarial puede ser consumida por prácticamente cualquier tecnología .NET (ASP.NET MVC / API / Core, WPF, Win Forms, WCF, UWP, WF, Console, etc.).

Además, nos gusta que nuestro nivel intermedio maneje las reglas comerciales y la lógica de validación para reducir nuestras dependencias en .NET MVC Framework. Por ejemplo, evitamos usar los ayudantes de validación de .NET MVC y, en cambio, confiamos en los nuestros. Este es otro factor que nos permite consumir fácilmente nuestra lógica empresarial desde cualquier tecnología .NET.

El diseño lógico de nuestro nivel medio de esta manera nos ha permitido lograr fácilmente esta arquitectura física:

ingrese la descripción de la imagen aquí

Fue escrito con Peasy.NET y nos ha servido bien a lo largo de los años. Tan bien de hecho que decidimos abrirlo.

Si alguien tiene curiosidad sobre cómo es nuestro nivel medio, aquí hay una muestra de una capa empresarial independiente del cliente. También muestra el consumo de varios clientes .NET (ASP.NET MVC, Web Api y WPF).

¡Espero que esto ayude a alguien!

ahanusa
fuente
En general, no es necesario reutilizar la lógica. Si digamos que decido usar ASP.NET Core MVC para Web API, nunca querré usar esa lógica empresarial en WPF o WinForms. Porque el cliente se comunicará con el servidor de todos modos. Poner la lógica empresarial y especialmente la lógica de acceso a la base de datos del lado del cliente es simplemente malo.
Konrad
Cuantos más niveles agregue, diría que disminuye la capacidad de mantenimiento y la capacidad de prueba. Al final, las pruebas de integración son más importantes.
Konrad
8

La lógica empresarial no debe ir en sus Vistas de modelos o Controladores. Debería haber una capa de lógica empresarial separada ; el único propósito de esta capa es manejar su lógica empresarial. Esto está más en línea con SOLID .

Si pusiera su lógica de negocios en MV o C, terminaría con un código que es difícil de probar / reutilizar.

¿Qué hay de poner la lógica en los modelos?

Esa es una mala solución.

Terminarás en un infierno de dependencia donde los objetos dependen de los objetos. ingrese la descripción de la imagen aquí

Incluso si tiene una función simple muerta, aún tendrá que satisfacer todas las dependencias para llamarla.

También hará que se pasen datos innecesarios y no utilizados sin ningún motivo. Esto también podría afectar el rendimiento dependiendo de qué tan mal se ponga.

También debo mencionar que las pruebas unitarias se vuelven un dolor de cabeza porque tienes que simular varios objetos solo para probar una función simple.

Se aplican los principios del código limpio

  1. Las clases / funciones toman solo lo que necesitan para hacer el trabajo.
  2. Las funciones deben tomar 3 parámetros o menos si es posible
  3. Nombrar clases / funciones / variables de forma inteligente (siga los estándares de Microsoft)
  4. No acople la lógica empresarial a la vista de modelo o al controlador

Controladores

En su controlador, debería poder usar la inyección de dependencia para inyectar la capa de lógica empresarial . Asegúrese de que su controlador solo se utilice para enrutar información a la capa de lógica empresarial. El controlador NO debe tener lógica comercial directamente en él. Cualquier validación debe ser manejada por IValidatableel modelo. Cualquier lógica empresarial debe enrutarse a una capa separada.

Cañón de Kolob
fuente
Aquí donde trabajo tenemos una capa empresarial y solo quería que no la tuviéramos. Las capas empresariales son un desastre, se supone que la lógica está en el modelo.
Mateus Felipe
@MateusFelipe Entonces, ¿dónde pones la lógica que requiere múltiples modelos (por ejemplo: Pago y Producto)? ¿Creas un nuevo modelo que tiene Paymenty Productcomo variable de instancia? ¿Cómo llamas a ese objeto? Si un modelo no se usa en una vista, ya no es un modelo. Es parte de una capa separada. Idealmente, esa clase que haga solo debe tomar lo que necesita de Pago y Producto para hacer su trabajo. Si solo necesita el productNamey el price, solo debe tomar esos dos parámetros, no todo el objeto.
Cañón de Kolob
4

La respuesta general que tengo es que la lógica empresarial normalmente encaja en dos categorías:

Lógica empresarial orientada a objetos: se modela como objetos (en el modelo), generalmente inyectados como repositorios.

Lógica empresarial procedimental: entra en un servicio con una interfaz que se puede inyectar en un controlador.

Controller Logic: Lógica que controla cómo se reciben los comandos y cómo se pasan a los modelos / servicios, luego cómo esos resultados se pasan a la vista.

Los controladores no deben tener lógica comercial , es una parte muy específica de un patrón de diseño para controlar cómo una interfaz de usuario transfiere la entrada a los modelos que manejan la lógica comercial (o servicios si sus problemas son de naturaleza más procedimental).

WhiteleyJ
fuente
2

También me gusta mantener limpios mis modelos (ref: @Mark Walsh). El problema de no poder reutilizar la lógica incrustada en los controladores puede superarse fácilmente mediante la inyección de dependencias o, si cree que habría demasiado de eso, exponga la lógica de su negocio / dominio a través de interfaces y utilice el patrón de fachada en los controladores. De esa manera, obtiene la funcionalidad que necesita, pero mantiene tanto los controladores como el modelo en buen estado y limpio.

Bob H
fuente
1

También preferiría mantener limpios los modelos. Los controladores MVC deben usarse solo para realizar llamadas y también deben mantenerse limpios. Entonces, dependiendo de su reutilización, sensibilidad y relevancia, la lógica empresarial se puede escribir en

1.WebApi Controller: La ventaja de usar un controlador webapi es que puede exponerlos como servicios más tarde a otros dispositivos, haciendo que su código sea reutilizable.

2. BAL / componente común: hay algunas lógicas que tienen un uso específico y no se pueden exponer como una API, se pueden insertar en esta clase.

3. Repositorio: Todas las consultas relacionadas con la base de datos se agregan a un repositorio. Puede haber un repositorio genérico que implemente todas las funciones (operaciones CRUD) o repositorios específicos para cada tabla. Dependiendo de las operaciones a realizar.

Nikitesh
fuente
1

Como escribió ahanusa, debe poner la lógica de su negocio en una DLL separada o en un directorio separado.
A menudo uso un directorio llamado Logics en el mismo nivel de Modelos y Controladores donde pongo clases que hacen lógica de negocios.
De esta forma dejo que tanto los modelos como los controladores se limpien.

Ryozzo
fuente
0

Sé que es una pregunta sobre MVC, pero creo que el ejemplo que estoy dando (API web) será útil.

Estoy desarrollando mi primera API web y estoy reutilizando la lógica empresarial de otras aplicaciones. Para ser específico, proviene de una DLL externa, por lo que mi API se usa solo para "hablar" con una solución SAP, recibir solicitudes de PO y enviar respuestas.

¿Cómo podría poner mi lógica (ya implementada) en mi controlador? No lo necesito Mi controlador solo recibirá, validará solicitudes y redactará respuestas para devolver datos.

Estoy trabajando con clases de ViewModel y todo lo que deben tener es una función de mapeo, solo para leer información de TransferObjects (que proviene de la DLL externa) y traducir a un ViewModel.

No me siento cómodo con mi aplicación (en este caso la API web) manteniendo la lógica empresarial, creo que la reutilización se perderá de esta manera.

Estoy tratando mi lógica empresarial como una dependencia que inyecto en el controlador.

He realizado muchas refactorizaciones en el legado para proporcionar una solución de prueba unitaria, tuve que crear muchas interfaces e implementar algunos patrones de diseño en el legado para proporcionar esta solución.

En mi punto de vista, la capa empresarial debe estar separada de la aplicación, preferiblemente en otra biblioteca de clases. Así tendrás implementado un concepto de separación real en tu aplicación.

Por supuesto, si su CORE (negocio) es su aplicación (API / WebSite) , las reglas de negocio se implementarán en sus clases MVC. Pero en el futuro, si desea desarrollar una nueva aplicación y algunas reglas comerciales son las mismas, apuesto a que tendrá muchos problemas solo para mantener la misma lógica implementada en ambas aplicaciones.

Otta Augusto
fuente