tutorial tag pagina net mvc establecer asp .net asp.net-mvc

.net - tag - web forms c#



¿Deben los controladores de una aplicación web ASP.NET MVC solicitar repositorios, servicios o ambos? (7)

Los controladores en mi aplicación web ASP.NET MVC están empezando a llenarse de lógica empresarial. Los ejemplos en la web muestran simples acciones de controlador que simplemente extraen datos de un repositorio y lo pasan a la vista. Pero ¿y si también necesitas apoyar la lógica de negocios además de eso?

Digamos, por ejemplo, que una acción que cumple una orden también necesita enviar un correo electrónico. ¿Pego esto en el controlador y copio / pego esta lógica a cualquier otra acción que también cumpla con las órdenes? Mi primera intuición sería crear un servicio como OrderFulfillerService que se encargaría de toda esta lógica y haría que la acción de controlador lo llamara. Sin embargo, para operaciones simples como recuperar una lista de usuarios u órdenes de la base de datos, me gustaría interactuar directamente con el repositorio en lugar de tener esa llamada envuelta por un servicio.

¿Es este un patrón de diseño aceptable? ¿Las acciones del controlador llaman a los servicios cuando necesitan lógica empresarial y repositorios cuando solo necesitan acceso a los datos?


La lógica de su negocio debe estar encapsulada en objetos comerciales: si tiene un objeto Order (y lo hace, ¿no es así?), Y una regla comercial indica que se debe enviar un correo electrónico cuando se complete el pedido, luego su método Fulfill ( o, si es más apropiado, el colocador para IsFulfilled) debe desencadenar esa acción. Probablemente tendré información de configuración que apunta al objeto comercial en un servicio de correo electrónico apropiado para la aplicación, o más generalmente a un servicio "notificador" para que otros tipos de notificación puedan agregarse cuando sea necesario.


Si va a tener una capa de negocios, entonces creo que es mejor tener solo la capa de negocios para hablar con la capa de datos. Puedo entender por qué en un caso de uso simple, la capa de presentación (el controlador) habla directamente con la capa de datos, pero una vez que identifica la necesidad de una capa empresarial aislada, se mezcla el uso de ambos en niveles superiores. ser peligroso.

Por ejemplo, ¿qué pasa si el controlador A llama un método en la capa empresarial para obtener una Lista del Objeto A (y este método aplica las reglas comerciales, tal vez algún filtrado o clasificación), pero luego aparece el Controlador B, necesita la misma información , pero se olvida de la capa de negocios y llama a la capa de datos directamente?


Ayudaría si pudiéramos dejar de ver este ejemplo una y otra vez ...

public ActionResult Index() { var widgetContext = new WidgetDataContext(); var widgets = from w in widgetContext.Widget select w; return View(widgets); }

Me doy cuenta de que esto no es útil para su pregunta, pero parece ser parte de una gran cantidad de demos que creo que pueden ser engañosas.


Puede parecer molesto ver mucho de esto en los servicios comerciales:

public Customer GetCustomer(int id) { return customerRepository.Get(id); }

Y es natural tener un fuerte impulso de eludir el servicio. Pero a largo plazo es mejor que permita que sus servicios comerciales sean intermedios entre controladores y repositorios.

Ahora, para una aplicación de tipo CRUD muy simple, puede hacer que sus controladores consuman repositorios directamente en lugar de pasar por servicios comerciales. Todavía podría tener algo como un EmailerService, pero IMO cuando se trata de buscar y hacer cosas con entidades, lo mejor es no mezclar y combinar el servicio comercial y las llamadas al repositorio en sus controladores.

En cuanto a tener entidades (objetos comerciales) llamar a servicios o cualquier componente de infraestructura, yo no haría eso. Prefiero mantener las entidades POCO y libres de dependencias.


Sus controladores (en el proyecto MVC) deberían llamar a sus objetos en el proyecto de servicio. El proyecto de servicios es donde se maneja toda la lógica de negocios.

Un buen ejemplo es esto:

public ActionResult Index() { ProductServices productServices = new ProductServices(); // top 10 products, for example. IList<Product> productList productServices.GetProducts(10); // Set this data into the custom viewdata. ViewData.Model = new ProductViewData { ProductList = productList; }; return View(); }

o con Inyección de Dependencia (mi favorito)

// Field with the reference to all product services (aka. business logic) private readonly ProductServices _productServices; // ''Greedy'' constructor, which Dependency Injection auto finds and therefore // will use. public ProductController(ProductServices productServices) { _productServices = productServices; } public ActionResult Index() { // top 10 products, for example. // NOTE: The services instance was automagically created by the DI // so i din''t have to worry about it NOT being instansiated. IList<Product> productList _productServices.GetProducts(10); // Set this data into the custom viewdata. ViewData.Model = new ProductViewData { ProductList = productList; }; return View(); }

Ahora ... ¿cuál es el proyecto de servicio (o qué es ProductServices)? esa es una biblioteca de clases con su lógica de negocios. Por ejemplo.

public class ProductServices : IProductServices { private readonly ProductRepository _productRepository; public ProductServices(ProductRepository productRepository) { _productRepository = productRepository; } public IList<Product> GetProducts(int numberOfProducts) { // GetProducts() and OrderByMostRecent() are custom linq helpers... return _productRepository.GetProducts() .OrderByMostRecent() .Take(numberOfProducts) .ToList(); } }

pero eso podría ser tan duro y confuso ... así que una versión simple de la clase ServiceProduct podría ser (pero realmente no lo recomendaría) ...

public class ProductServices { public IList<Product> GetProducts(int numberOfProducts) { using (DB db = new Linq2SqlDb() ) { return (from p in db.Products orderby p.DateCreated ascending select p).Take(10).ToList(); } } }

Ahí vas. Puede ver que toda la lógica está en los proyectos del Servicio, lo que significa que puede reutilizar ese código en otros lugares.

¿Dónde aprendí esto?

Desde los medios y tutoriales MVC StoreFront de Rob Conery . Lo mejor desde el pan en rodajas. Sus tutoriales explican (lo que hice) en detalle con ejemplos completos de código de solución. Él usa Dependency Injection, que es SOO kewl ahora que he visto cómo lo usa, en MVC.

HTH.


No estoy seguro acerca de usar servicios para esto.

Según entiendo, uno de los principios de DDD (que estoy leyendo en este momento) es que los objetos de dominio están organizados en agregados y que cuando se crea una instancia de la raíz de agregado, solo se puede tratar directamente con los objetos dentro del Agregado (para ayudar a mantener un claro sentido de responsabilidad).

Crear el agregado debe hacer cumplir cualquier invariante, etc.

Tomando un ejemplo de una clase de Cliente, el Cliente podría ser la raíz Agregado y otra clase dentro del Agregado podría ser Dirección.

Ahora, si desea crear un nuevo Cliente, debería poder hacerlo utilizando el constructor del Cliente o una fábrica. Hacer esto debería devolver un objeto que sea completamente funcional dentro del límite Agregado (por lo que no puede tratar con Productos ya que estos no son parte del Agregado pero puede manejar Direcciones).

La base de datos es una preocupación secundaria y solo entra en juego para persistir el Agregado en la base de datos o recuperarla de la base de datos.

Para evitar la interacción directa con la base de datos, puede crear una interfaz de repositorio (como se explicó) que, dada una instancia de Cliente (que incluye una referencia a Dirección), debe poder conservar el Agregado en la base de datos.

El punto es que la interfaz del Repositorio es parte de su modelo / capa de dominio (la Implementación del Repositorio no lo es). El otro factor es que el repositorio probablemente debería terminar llamando al mismo método de "creación" como si estuvieras creando un nuevo objeto (para mantener invariantes, etc.). Si está utilizando un constructor, esto es bastante simple, ya que terminará llamando al constructor cuando el repositorio "crea" el objeto a partir de datos de todos modos.

La capa de aplicación puede comunicarse directamente con el dominio (incluida la interfaz del repositorio).

Entonces, si quieres crear una nueva instancia de un objeto, puedes por ejemplo

Customer customer = new Customer();

Si la aplicación necesita recuperar una instancia del cliente del repositorio, no hay ninguna razón en particular que pueda pensar para que no llame ...

Customer customer = _custRepository.GetById(1)

o...

Customer customer = _custRepository.GetByKey("AlanSmith1")

En última instancia, terminará con una instancia del objeto Cliente que funciona dentro de sus propios límites y reglas tal como lo haría si creara el nuevo objeto Cliente directamente.

Creo que los servicios deberían reservarse para cuando la "cosa" con la que intentas trabajar no sea un objeto. La mayoría de las reglas (restricciones, etc.) se pueden escribir como parte del Objeto del Dominio.

Un buen ejemplo está en el DDD PDF rápido que estoy leyendo en este momento. Allí tienen una restricción en un objeto Bookshelf, por lo que solo puede agregar tantos libros como contenga el estante.

Llamar al método AddBook en el objeto BookShelf comprueba que el espacio esté disponible antes de agregar el libro a la colección BookShelf de objetos Book. Un ejemplo simple, pero la regla de negocio es impuesta por el objeto de dominio en sí.

¡No digo que ninguna de las anteriores sea correcta por cierto! ¡Estoy tratando de entender todo esto en este momento!


Bueno, depende de ti, me gusta mantener los controladores lo más simples posible, y para archivar esto necesito encapsular la lógica de los negocios en una capa separada, y aquí está lo más importante, principalmente tienes 2 opciones, asumiéndote están utilizando Linq2SQL o Entity Framework:

  • Puede usar los métodos de extensión y la clase parcial para validar sus Modelos justo antes de Guardar los cambios (método de ganchos, puede ver un ejemplo de esto en la muestra de Nerdinner por Scott Gu).

  • La otra forma (y mi favorita, porque siento más control sobre el flujo de la aplicación), es usar una capa totalmente separada para la lógica de negocios como la Capa de Servicios (puede ver esta aproximación en la serie de tutoriales de Stephen Walther en asp.net / zona mvc).

Con estos dos métodos obtienes DRY y limpias tus controladores que de otra manera serían desordenados.