Introducción
En esta publicación, exploraremos un tema del que ya hemos hablado en ocasiones anteriores, pero esta vez queremos profundi...
For further actions, you may consider blocking this person and/or reporting abuse
Hola Isaac, gracias por escribir el tutorial, me ha servido para mantener un proyecto.
De igual manera me gustaría dar mis observaciones sobre su artículo, que en realidad no es para generar una discusión o algo por el estilo.
Citaré algunos párrafos:
Los inconvenientes que mencionan los párrafos no es un problema en sí del patrón de Repositorio o de agente de servicio, sino de las malas prácticas que se aplican al proyecto. Sí sigue los principios de ingeniería como el de dependencias explícitas y de responsabilidad única, se puede lograr tener una clase altamente cohesiva y de bajo acoplamiento.
Un ejemplo simple obtenido del código de nopCommerce:
CustomerRegistrationService.cs
La clase
CustomerRegistrationService
está saturada, tiene 20 dependencias! Solo mirando el constructor es una señal que la clase debe ser dividida por partes, para así cumplir con el principio de responsabilidad única, sin embargo, para lograrlo no es necesario crear un método de servicio por clase:ValidateCustomerHandler.Handle
,RegisterCustomerHandler.Handle
, etc.CQRS tiene sus ventajas. El problema es cuando se aplica de manera tan estricta y rígida, por lo que esto conduce a tener un montón de archivos de clases por cada acción que realice un punto final (endpoint):
GetProductsQueryHandler
,EditProductsQueryHandler
, etc. La desventaja de hacerlo así es que cuando se necesite realizar alguna modificación (o estudio) a una funcionalidad, se debe de acceder a muchas clases (es como moverte entre clases). Para Dave es una desventaja, tal vez para otros no.Me gusto mucho esta implementación de CQRS: github.com/Odonno/cqrs-dotnet-core... (no es tan estricta).
Este comentario es solo mi punto de vista. Saludos!
Revisando el repositorio de Odonno realmente yo creo ya son opiniones de cada quien, pero aquí vuelve a ser lo mismo que busco evitar, aunque sigas el single responsability, la clase
ParkingCommandHandler
la vas a tener que partir para no tener una clase gigante como laCustomerRegistrationService
.Tener varios métodos
Handle
en una clase también me causa conflicto, ya que la forma en la que navego el código, no se me haría práctico, aunque estoy seguro que es algo trivial a lo que te puedes acostumbrar.Te recomiendo esta charla de Jimmy Bogard, esa es una razón por la que decido hacer una clase por feature, a veces hay features muy pesados que tienen mucha lógica, tener todo junto en ese feature me resulta práctico y fácil de entender. Entiendo que puedes aplicar mil principios y formas para limpiar el
CommandHandler
y que no se convierta en un "big ball of mud" al final como dices, son opiniones jeje.Saludos ya por última vez!
Sí, utilizar una clase por feature cuando la lógica es demasiada compleja, me parece bien. Sin embargo, he visto proyectos que por cada feature "simple" crean una clase y apenas tiene cinco líneas de código (o peor aún, una sola línea), entonces, al menos para mí, me parece tedioso estar moviéndome entre tantos archivos que al final tienen una lógica demasiada simple.
Y otro detalle que he visto en estos proyectos, es que usan nombres de clases que representan acciones, por ejemplo
GetProductsByIdHandler
, al menos para mí lo veo confuso, ya que estoy acostumbrado a nombrar clases como sustantivos (tal vez documentando las clases puede ser de ayuda para saber cuál es su intención).De igual manera, gracias por responder Issac.
Saludos!
Volviendo a leer tu comentario:
Realmente es lo contrario, un archivo tiene de responsabilidad un feature y para cambiar ese feature hay que irnos solamente a ese archivo, no a uno gigante como un repositorio (como lo menciona el post)
Tal vez dependiendo del equipo y el tipo de proyecto, se deben de evaluar la estructura del proyecto, pero al final siempre se deben de seguir los principios de ingeniería como lo mencionas.
Saludos nuevamente!
Muchas gracias por tus comentarios, son totalmente válidos.
Tal vez, la razón por la que a mi me funciona tener cientos de archivos como lo mencionas, es por mi uso de navegación en el editor.
Ejem. En VS Code
CTRL + P
y luego mi búsqueda nada explícita, ejem.GetProdQH
haciendo referencia aGetProductsQueryHandler
. El buscador lo hace muy bien y no es necesario escribir toda la palabra, tal vez esa es la razón por la que a mi o en mi equipo no nos resulta un problema tener muchos archivos.Gracias por compartir nuevamente. Saludos!
reddit.com/r/csharp/comments/rxxg5.... Según esto usar mediatr es 52x más ineficiente que usar un servicio directamente,y creo que para un web API está métrica es importante,a veces escribir menos código no significa más eficiencia a coste de rendimiento
Mira este vídeo de Nick y los comentarios de zshazz (en el hilo de reddit) son interesantes y están acorde al vídeo.
Es mentira que sea 52 veces más lento.
Lo que sí es real, sí existe una degradación del performance, pero en ciertos escenarios es donde vas a "sentirlo". Por los beneficios que agrega MediatR y el tipo de aplicación, para mi sigue siendo una opción 99% recomendada. No todos programamos el siguiente Netflix que requiere 1,000 microservicios, pero sí programamos para estar listos a adaptarnos a eso.
Gracias por tu comentario, siempre es bueno indagar y cuestionar, por que eso nos lleva a investigar más. Saludos.
Excelente aporte. Esperare con paciencia la parte 2. Gracias por compartir
Gracias, ya está arriba la parte 2. Saludos
Excelente articulo, tuve que agregar en el constructor de MyAppDbContext esta instruccion :
public MyAppDbContext(DbContextOptions options) : base(options)
{
Database.EnsureCreated();
}
Saludos
Tienes razón, no expliqué la parte donde hice la migración y actualización de la BD...
Gracias!
Antes que nada quiero agradecer estos posts, es valiosa la información que nos compartes se aprende mucho!
En el CreateProductCommandHandler , como seria la modificacion si necesito agregar una lista de objectos al CreateProductCommand , en lugar de un solo objeto.
Gracias
Pues tendrías que incluirlo en el
IRequest
como un listado, una idea es:Y en el Handler, iteras la creación del producto.
Pueden haber más formas, pero algo así lo haría yo
Saludos!!