Me cuesta entender por qué tantos ejemplos en la web usan MediatR al explicar los patrones de CQRS, al tratar con comandos y consultas.
Casi en todas partes veo ejemplos en los que MediatR maneja los comandos y las consultas, pero no veo ningún beneficio aparte de no tener que registrar cada comando o consulta en el contenedor de inyección de dependencia. Pero luego debe implementar objetos de consulta (heredando IRequest), controladores de consulta y objetos de respuesta de consulta para que en su método de controlador de API pueda llamar a _mediatr.Send(queryObject)
.
¿Por qué no simplemente usar la inyección de dependencia para inyectar el objeto de consulta en el controlador API al que puede llamar directamente a los métodos "obtener"? Me gusta:
[HttpGet] [Route("getall")] public async Task<IncidentQueryResult> GetAll(int page = 0, int pageSize = 25) { var result = await _incidentQueries.GetIncidents(page, pageSize); return result; }
en lugar de:
[HttpGet] [Route("getall")] public async Task<IncidentQueryResult> GetAll(int page = 0, int pageSize = 25) { var query = new IncidentQuery(page, pageSize); var result = await _mediatr.Send(query); return result; }
Luego, dentro del método GetIncidents
, hay una llamada sql directa a la base de datos y la asignación de resultados a objetos C#. Llano y simple.
Para mí, el uso perfecto y razonable de la biblioteca MediatR es manejar eventos de dominio. Al implementar DDD, intento configurar un proyecto de la forma que se presenta a continuación. Cada rectángulo es un proyecto diferente en la solución. Las flechas representan referencias:
Imaginemos un escenario: la creación de un objeto de dominio necesita incrementar un contador almacenado en otro objeto de dominio (un agregado diferente)
Entonces, MediatR para mí funciona solo en la capa de Infraestructura y Aplicación .
¿Las personas solo usan MediatR para comandos y consultas solo por usarlo? Para mí, parece que agregar comandos y controladores de consultas, consultas y solicitudes de comandos y tipos de respuesta solo agrega más código que no tiene valor real y solo lo hace menos comprensible.
Aquí hay algunos enlaces que visité:
Muchos sitios en mi idioma nativo también tienen esto.
Tener demasiados controladores utilizados en su aplicación dificulta leer lo que hace su aplicación y qué desencadena qué. Lo que vi fue que las personas manejaban eventos de dominio en la capa de Comandos, pero el dominio probablemente no debería enviar comandos directamente.
¿Necesita usar MediatR en CQRS?
MediatR
es solo una biblioteca para resolver una necesidad específica. Como dice el repositorio:
Mensajería en proceso sin dependencias.
Command Query Responsibility Segregation (o CQRS
) es un patrón de diseño que se puede implementar de muchas maneras. Básicamente, siempre que sus lecturas y escrituras sean independientes, está "siguiendo" este patrón.
Así que no, puede crear una aplicación completa que sea "compatible" con CQRS
sin usar MediatR
ni ninguna otra biblioteca de mensajería. Dicho esto, también puede compilarlo como un archivo masivo. CQRS
es simplemente una de las muchas herramientas para administrar código que puede implementar según sus necesidades.
También puede usar MediatR
para mensajes en proceso y no aplicar el patrón CQRS
.
Dicho esto, una razón común por la que es probable que vea tutoriales, blogs y otros recursos de .NET CQRS
que usan MediatR
(o una biblioteca de mensajería similar) es que, en general, cualquier aplicación que use CQRS
también querrá lo siguiente:
MediatR
generalmente resuelve ambos problemas muy bien al separar la ejecución del comando de su implementación (que puede vivir en un proyecto separado)
En su caso, por ejemplo, Presentation
debe tener un conocimiento de la implementación de la base de datos, así como su esquema para ejecutar consultas y asignarlas a los recursos de la base de datos en lugar de dejar eso solo como una preocupación de la infraestructura. En mi experiencia, esto puede generar mucho código duplicado o muchos acoplamientos no deseados entre proyectos. Es mejor tener la capa de presentación centrada en la presentación y enviar un mensaje a cualquier servicio (no importa cuál o tratar de registrarlos en MediatR
) que pueda brindarle información de consulta a pedido.
Esencialmente, en su diagrama MediatR
(y/o NServiceBus
, Brighter
, MassTransit
, Rebus
... si necesita escalar más allá de un solo proceso) actuaría como una forma de controlar el flujo de datos y separar al cliente de Query/Command de es manejador.
Así que finalmente para responder:
¿Las personas solo usan MediatR para comandos y consultas solo por usarlo?
Sí y no, en su mayoría lo usan como una buena práctica separada que funciona muy bien junto con el patrón CQRS
para controlar su flujo de dependencia. Aunque tiene razón en que, para muchos principiantes en estas ideas, pueden unir las dos de maneras que no son necesarias ni recomendadas.
Le recomiendo que eche un vistazo a su otro trabajo en Clean Code
para comprender cómo todas estas piezas se combinan para crear (lo que él llama) un "pozo de éxito" para los futuros desarrolladores que trabajan en el proyecto. Tiene un repositorio de plantillas aquí y varias charlas en línea al respecto: https://github.com/jasontaylordev/CleanArchitecture
Tienes razón en que la biblioteca MediatR
y DDD
/ CQRS
por alguna razón se han convertido en sinónimos. Pero estos son mutuamente excluyentes. Puede usar MediatR
sin diseño controlado por dominio y viceversa. El enfoque se ha vuelto popular debido a su simplicidad y disponibilidad de una variedad de proyectos de muestra.
Una de las razones por las que MediatR
es popular es porque lleva la lógica comercial y de aplicación fuera de Controller
, que, históricamente, son difíciles de escribir pruebas.
El patrón MediatR
le brinda una manera fácil de separar estas preocupaciones. También hay otras alternativas disponibles, como Service Stack . El mismo concepto también podría usarse con Vertical Slice Architecture que tiene como objetivo separar el código por características.
Del mismo modo, mientras implementa el diseño basado en dominios, puede elegir libremente lo que funciona para usted. Sin embargo, MediatR
le brinda la flexibilidad de mantener su código extensible (principio de apertura y cierre). En el futuro, si necesita reemplazar esta arquitectura con un Message Bus real utilizando NServiceBus
u otros mecanismos, no necesita cambiar su base de código completa.
La mayor desconexión que veo entre MediatR y CQRS es que tanto los comandos como las solicitudes pasan por el método IMediator.Send()
. Si, al menos, hubiera abstracciones separadas para comandos y consultas, vería la conexión con CQRS.
MediatR es, en el mejor de los casos, neutral con respecto a CQRS. La conexión implícita entre MediatR y CQRS es inexistente, y los artículos que ofrecen una explicación no lo hacen.
Por ejemplo, una de las publicaciones relaciona MediatR con CQRS de esta manera:
En CQRS, las responsabilidades de las consultas y los comandos las asumen sus respectivos controladores y se vuelve bastante difícil para nosotros conectar manualmente un comando con su controlador y mantener las asignaciones.
La implicación es que al aprender a asignar comandos y consultas a los controladores, estamos aprendiendo a implementar CQRS. Pero CQRS se trata de separar consultas y comandos. No tiene nada que ver con si una aplicación asigna comandos a los controladores o cómo lo hace. Entonces, los artículos tienden a describir CQRS y luego brindan detalles de implementación que no tienen nada que ver con CQRS. No obstante, la popularidad de tales artículos refuerza la idea de que MediatR y CQRS están intrínsecamente conectados.
Esto no es un problema con MediatR. Algunos artículos omiten MediatR y solo se refieren al patrón mediador, aún sin establecer la conexión entre CQRS y el patrón mediador. Ese parece ser el malentendido central. Podríamos usar cualquier cantidad de patrones de diseño en una aplicación que aplica CQRS, pero describir tanto el patrón de diseño como CQRS en el mismo artículo no significa que usar el patrón conduzca a CQRS.
En el peor de los casos, la interfaz IMediator
dificulta CQRS porque no distingue entre comandos y solicitudes. Ni MediatR ni ninguna otra abstracción pueden evitar que un controlador de consultas ejecute un comportamiento de comando, pero si estamos separando comandos y consultas, el corazón de CQRS, la abstracción debería admitir esa separación.