Arquitectura / capas de proyectos .NET MVC

11

Al planificar la arquitectura para una aplicación web MVC de mediana y gran escala, ¿cómo implementa las capas para que estén tan desacopladas como sea posible y fáciles de probar? (básicamente siga las mejores prácticas) Digamos que estoy usando el código primero como mi acceso a datos.

Lucho con qué definir como "lógica de negocios" y cómo se supone que interactúa con la capa de datos. Tomando una aplicación de venta de vehículos como ejemplo, ¿sería la lógica de negocios clases que realizaran tareas como calcular la banda de impuestos para vehículos dados, comparar estadísticas de millas por galón, etc.? En cuanto a las entidades comerciales (por ejemplo, automóviles, furgonetas, motocicletas), las incluiría en la capa de datos junto con mi DataContextclase.

Además, ¿qué constituiría la lógica de la aplicación en lugar de la empresa? Supongo que cosas como validaciones de entrada de sesión / usuario?

Entonces, por ejemplo, un controlador de automóvil podría devolver un resultado de acción / vista que enumera los diez automóviles principales filtrados por tipo y mejor mpg. Entonces, digamos que tengo un ICarRepository'carRepo' inyectado en mi controlador (usando el patrón de repositorio / DI), filtro mis autos desde un parámetro del método de acción, por ejemplovar cars = carRepo.getCarsByType("hatchback");

Así que mantuve el conocimiento de acceso a datos fuera de mi controlador usando un repositorio, ahora para mantener la lógica de negocios fuera del controlador usando un modelo de dominio - var result = new MpgCalculator (cars); - Digamos que necesito la clase de calculadora porque necesita realizar una lógica adicional para calcular la mejor eficiencia de combustible, más que solo cargar / filtrar entidades del DB. Entonces, ahora tengo un conjunto de datos para mi vista que representa que utilizó un repositorio para recuperar de la capa de acceso a datos y un objeto específico de dominio para procesar y realizar tareas relacionadas con el negocio en esos datos.

¿Estoy cometiendo errores aquí? ¿todavía necesitamos usar el patrón de repositorio o puedo simplemente codificar contra una interfaz para desacoplar el ORM y probar? Sobre este tema, dado que mis clases concretas de acceso a datos dbcontext están en la capa de datos, ¿deberían las definiciones de interfaz entrar en la capa de dominio / negocio, lo que significa que si la tecnología de acceso a datos cambia alguna vez, mis otras capas no se verán afectadas?

Por lo que he estudiado hasta ahora, mi estructura se ve así:

Aplicación de Internet MVC -> El proyecto estándar de Internet - los modelos aquí son ViewModels

Capa de dominio / negocio -> clases / modelos específicos de negocio que los controladores pueden usar para procesar entidades de dominio desde la capa de datos antes de pasar a las vistas relevantes

¿Es necesaria la abstracción del repositorio? -> Escucho mucho debate sobre esto, especialmente cuando uso un ORM

Capa de datos -> Clases de entidad (automóvil, furgoneta, motocicleta), DbContext - Capa de tecnología de acceso a datos concretos

Michael Harper
fuente

Respuestas:

26

Tiene muchas partes móviles en su pregunta, tocando muchos conceptos, pero aquí está mi consejo básico cuando se trata de cómo pensar en una aplicación MVC de mediana a gran escala:

Presentación <---> Business Logic <---> Acceso a datos

En primer lugar, es mejor no pensar en la aplicación como "una aplicación MVC". Es una aplicación que utiliza el patrón MVC como su componente de presentación. Pensarlo de esta manera lo ayudará a separar sus preocupaciones de lógica de negocios de sus preocupaciones de presentación . Quizás esté bien que las aplicaciones pequeñas apilen todo hasta el acceso a la base de datos en la estructura MVC, pero rápidamente se volverá insostenible para una aplicación de mediana a grande.

MVC (Presentación)

En su aplicación, el componente ASP.NET MVC debe tratar con la transformación de los datos comerciales para fines de visualización (Modelos), mostrar la interfaz de usuario (Vistas) y problemas de comunicación como enrutamiento, autenticación, autorización, validación de solicitudes, manejo de respuestas y como (controladores). Si tiene un código que hace otra cosa, entonces no pertenece al componente MVC .

Repositorio / ORM (acceso a datos)

También en su aplicación, la capa de acceso a datos debe preocuparse por recuperar y almacenar datos persistentes. Por lo general, se trata de una base de datos relacional, pero hay muchas otras formas en que los datos pueden persistir. Si tiene código que no lee o almacena datos persistentes, entonces no pertenece a la capa de datos . Compartí mis pensamientos sobre la discusión de ORM / Repository anteriormente sobre SO, pero para resumir, no considero que un ORM sea lo mismo que un Repository, por varias razones.

Lógica de negocios

Entonces, ahora tiene su capa de presentación (MVC) y su capa de datos (repositorio u ORM) ... Todo lo demás es su capa de lógica de negocios (BLL). Todo su código que decide qué datos recuperar, o realiza cálculos complicados, o toma decisiones comerciales, debe estar aquí. Generalmente organizo mi lógica de negocios en forma de 'servicios', a los que mi capa de presentación puede recurrir para hacer el trabajo solicitado. Todos mis modelos de dominio existen aquí.

Su enfoque

Aquí es donde tu enfoque se rompe un poco para mí. Describe su controlador MVC como el lugar donde obtendría los datos del repositorio y solicita al MPGCalculator que haga algún trabajo, etc. No quisiera que mi controlador hiciera nada de esto, sino que delegaría todo esto a un servicio en el BLL.

En otras palabras, no inyectaría un repositorio y MPGCalculator en el controlador, eso le da demasiada responsabilidad al controlador (ya está manejando todas las cosas del controlador que mencioné anteriormente). En cambio, tendría un servicio en el BLL que manejaría todo eso y pasaría los resultados al controlador. El controlador puede luego transformar los resultados al modelo correcto y pasarlos a la vista correcta. El controlador no tiene ninguna lógica de negocios, y las únicas cosas inyectadas en el controlador serían los servicios BLL apropiados.

Hacerlo de esta manera significa que su lógica de negocio (por ejemplo, dado un conjunto de vehículos, calcular el MPG y clasificar de mejor a peor ) es independiente de las preocupaciones de presentación y persistencia. Por lo general, estará en una biblioteca que no conoce ni le importa la estrategia de persistencia de datos ni la estrategia de presentación.

Eric King
fuente
Hola Eric, excelente respuesta: con respecto a los repositorios, supongo que las clases concretas vivirían en la capa de acceso a datos y el 'ICarRepository', etc. en la capa de negocios / servicios. ¿Entonces podría inyectar servicios en mi controlador que pueden contener 1 o más repositorios dependiendo de los requisitos?
Michael Harper
@MichaelHarper Sí, eso suena como una forma perfectamente buena de hacerlo.
Eric King
1
Si bien la autenticación es una preocupación del controlador (las diferentes IU se autentican de manera diferente), diría que la autorización es lógica empresarial y pertenece a la capa empresarial. ¿Estás de acuerdo?
Tom
1
@tom Sí, tienes un buen punto. Estaba pensando en una autorización simple ya que el usuario tiene acceso a esta ruta , pero puede haber mucho más que eso. La parte de "mucho más" pertenece a la capa empresarial.
Eric King
1
@HunterNelson Si está asignando a un modelo de vista, entonces la asignación debe ocurrir donde está la vista, en la capa de presentación. No tendría sentido en ningún otro lugar.
Eric King
0

Parece que todo es correcto para su estructura. Lo único de lo que no estoy seguro es que mencione que los modelos en MVC son "ViewModels" y que sus controladores hablan con la capa de dominio. Creo que esto tiene sentido si su patrón predeterminado es usar el controlador para acceder a la capa de dominio y luego usar sus "ViewModels" como compilaciones de información más específicas de la vista de múltiples entidades de dominio, como tiene sentido para esa vista en particular. Si eso es lo que estás haciendo, entonces es probable que estés bien.

Hay una escuela de pensamiento que debería tener una abstracción completa de su capa de dominio en su aplicación MVC si va a tener alguna. Personalmente, la idea de hacerlo en una aplicación empresarial me causa graves dolores mentales.

Prefiero usar el patrón de repositorio para administrar el acceso a la capa de datos, ya que mejora la capacidad de prueba y la flexibilidad. Las dos cosas que tienden a hacer los cambios más drásticos son la interfaz de usuario y la base de datos. Imagínese si parte de la información que está extrayendo directamente de la base de datos se cambia para que tenga que recuperarse de una llamada de servicio en lugar de una llamada a la base de datos, o parte de la información se transfiere a una base de datos diferente que requiere un .edmx diferente expediente. El patrón de repositorio proporciona abstracción para apoyar esto.

wpenberthy
fuente
Gracias por la respuesta William 😊 Consideraría mis objetos de negocio / lógica y entidades de dominio como 'modelos' que el controlador usa para procesar las acciones del usuario y ver modelos como ver modelos específicos que podrían contener grupos de modelos, etc.
Michael Harper