Architecture-center: ¿Cuál es la diferencia entre ProductsCommandHandler (CQRS) y ProductRepository (arco tradicional)?

Creado en 15 nov. 2019  ·  5Comentarios  ·  Fuente: MicrosoftDocs/architecture-center

Hola,
Gracias por escribir este artículo sobre CQRS. Estoy tratando de entender cómo se ve la implementación y estaba revisando los ejemplos. El código de repositorio de muestra proporcionado en el ejemplo se ve muy similar a cómo normalmente se escribiría un repositorio, excepto por la convención de nomenclatura. Por ejemplo, creo que el repositorio que se muestra a continuación es el mismo que ProductsCommandHandler y probablemente una diferencia sería que aquí no hay GetProduct que normalmente agregaríamos. ¿Podría explicar lo que no obtengo aquí?

public class ProductRepository {
  void AddNewProduct(Product newProduct) {
    ...
  }
  void RateProduct(int productId, int userId, int rating) {
    var product = repository.Find(productId);
    if (product != null)
    {
      product.RateProduct(userId, rating);
      repository.Save(product);
    }
  }
}

Detalles del documento

No edite esta sección.

Pri1 architecture-centesvc assigned-to-author cloud-fundamentalsubsvc product-question triaged

Comentario más útil

@martinmthomas De hecho, el controlador de comandos no es un repositorio. Utiliza un repositorio.

El controlador de comandos está destinado a "procesar los comandos reales, si es adecuado". En el ejemplo, la clase ProductsCommandHandler recibe un IRepository<Product> en su constructor.

Supongamos que el usuario calificará el producto 5555 con 4 estrellas.

  • El usuario ve una interfaz. Digamos una página web (podría ser un programa .exe local o lo que sea, imaginemos un sitio web).
  • El usuario hace clic en un botón. Digamos que esto activa un POST AJAX que va a una ruta / tarifa con datos {"product":"5555","stars":4}
  • La ruta POST tiene un controlador. Este controlador está asociado a una operación de "escritura" ya que proviene de una llamada POST.
  • El controlador aquí en un enfoque clásico simplemente cargaría el repositorio de productos clásico y cargaría el producto 5555. Luego dígale al producto "ahora tiene una calificación de 4 estrellas" y ahorre.
  • En este enfoque, el controlador crea el comando RateProduct y completa el producto 5555, asterisco 4. En el ejemplo, también completa quién está calificando.
  • En este momento, el controlador "envía el comando" al modelo de escritura. Tiene 2 opciones aquí: Invocar un controlador de comandos o ponerlo en cola.
  • Di que no tienes cola. Luego obtiene el CommandHandler en su controlador (probablemente por inyección de dependencia) y simplemente coloca el comando allí: h.Handle (c); donde c es el comando RateProduct.
  • Digamos que tienes cola. Luego obtiene la cola en su controlador (probablemente por inyección de dependencia) y simplemente coloca el comando allí: q.Queue (c);
  • En este último caso, el oyente de la cola sacará el comando de la cola y llamará al controlador de comandos en su lugar.
  • Como tercera opción (recomendada), su controlador no elige si el comando se está procesando de forma sincrónica (obtener el controlador) o asincrónica (obtener la cola), pero debe utilizar un "enfoque genérico" que es utilizar un bus de comandos donde los comandos están situados. Digamos que el bus de comando es b, entonces haría b. Enviar (c); donde c es el comando RateProduct.
  • Con este tercer enfoque, coloca el controlador de comandos al final del bus y puede configurar middlewares para "quitar el comando del bus y colocarlo en la cola".

Entonces, 3 opciones: a) Obtener el controlador, b) Obtener la cola, c) Obtener un bus y dejar que decida enviar el comando al controlador oa la cola.

Sea cual sea el enfoque que haga ... la principal diferencia acerca de "un repositorio clásico" es que NUNCA está invocando el repositorio en el controlador. El controlador no "sabe cómo administrar entidades" (repositorio clásico) pero sí sabe "cómo administrar INTENCIONES DE HACER COSAS a las entidades" (manejador de comandos).

Luego es el controlador de comandos que, como su nombre lo dice, "maneja el comando" que vino de algún lugar (un controlador web html, un controlador de API, una línea de comandos, lo que sea) que se envió como una intención y es el CommandHandler (que pertenece al lado de escritura) que decide qué hacer con ese comando.

Por ejemplo, CommandHandler puede usar un repositorio para obtener el producto, establecer el estado y guardar (como en el ejemplo), pero también puede escribir en un registro de eventos, activar elementos para actualizar los lados de lectura, activar conectores externos o lo que sea.

La interfaz de usuario basada en tareas del usuario para calificar el producto y su controlador como AGNÓSTICO de "dónde" se coloca o almacena el sistema de estrellas / calificación. Imagine que diseña un sistema desde cero y la clase Product ya contiene el método RateProduct () como en el ejemplo. Bien.

Pero ... ¿qué pasa si tiene un sistema heredado con un enfoque "Antiguo" del Producto? En el "modelo" (es decir, Business Mind) no hay "calificación" del producto. En cambio, el encargado de marketing quiere "agregar" calificación al producto existente, pero toda la empresa está de acuerdo en que se trata de una "cosa externa". ¿Usaría el repositorio de productos? ¿O quizás otro almacenamiento auxiliar para que no "toque" el Producto y el Repositorio de productos que ya están funcionando y completamente probados?

Si usa comandos, no le importa al controlador de escritura. La web, la API y la CLI enviarán el "comando" al controlador de comandos (ya sea directamente, a través de la cola o mediante un bus de comandos que se enrutará a su vez al controlador oa una cola) y se olvidan. Luego, el manejador de comandos decidirá qué hacer con el "RateProductCommand" en un punto centralizado y pequeño de su código fuente, desacoplando la forma en que esto se maneja del código de la aplicación, ganando así la mantenibilidad.

Luego, el manejador decidirá si es adecuado utilizar ProductRepository o cualquier otro enfoque para almacenar "la calificación de un producto".

Entonces, para responder:

CommandHandler => no tiene nada que ver con las entidades. Maneja las "intenciones del usuario" (que en última instancia podría ser alterar entidades; por lo que lo más probable es que el controlador de comandos use un repositorio).
Repositorio => el almacenamiento real de una determinada entidad.

Espero poder ayudar.
Xavi.

Todos 5 comentarios

@martinmthomas ¡ Gracias por tu pregunta! Revisaremos y proporcionaremos una actualización según corresponda.

@MikeWasson ¿Alguna idea aquí?

AB # 160217 - Gracias por informar - este problema está en revisión

@martinmthomas De hecho, el controlador de comandos no es un repositorio. Utiliza un repositorio.

El controlador de comandos está destinado a "procesar los comandos reales, si es adecuado". En el ejemplo, la clase ProductsCommandHandler recibe un IRepository<Product> en su constructor.

Supongamos que el usuario calificará el producto 5555 con 4 estrellas.

  • El usuario ve una interfaz. Digamos una página web (podría ser un programa .exe local o lo que sea, imaginemos un sitio web).
  • El usuario hace clic en un botón. Digamos que esto activa un POST AJAX que va a una ruta / tarifa con datos {"product":"5555","stars":4}
  • La ruta POST tiene un controlador. Este controlador está asociado a una operación de "escritura" ya que proviene de una llamada POST.
  • El controlador aquí en un enfoque clásico simplemente cargaría el repositorio de productos clásico y cargaría el producto 5555. Luego dígale al producto "ahora tiene una calificación de 4 estrellas" y ahorre.
  • En este enfoque, el controlador crea el comando RateProduct y completa el producto 5555, asterisco 4. En el ejemplo, también completa quién está calificando.
  • En este momento, el controlador "envía el comando" al modelo de escritura. Tiene 2 opciones aquí: Invocar un controlador de comandos o ponerlo en cola.
  • Di que no tienes cola. Luego obtiene el CommandHandler en su controlador (probablemente por inyección de dependencia) y simplemente coloca el comando allí: h.Handle (c); donde c es el comando RateProduct.
  • Digamos que tienes cola. Luego obtiene la cola en su controlador (probablemente por inyección de dependencia) y simplemente coloca el comando allí: q.Queue (c);
  • En este último caso, el oyente de la cola sacará el comando de la cola y llamará al controlador de comandos en su lugar.
  • Como tercera opción (recomendada), su controlador no elige si el comando se está procesando de forma sincrónica (obtener el controlador) o asincrónica (obtener la cola), pero debe utilizar un "enfoque genérico" que es utilizar un bus de comandos donde los comandos están situados. Digamos que el bus de comando es b, entonces haría b. Enviar (c); donde c es el comando RateProduct.
  • Con este tercer enfoque, coloca el controlador de comandos al final del bus y puede configurar middlewares para "quitar el comando del bus y colocarlo en la cola".

Entonces, 3 opciones: a) Obtener el controlador, b) Obtener la cola, c) Obtener un bus y dejar que decida enviar el comando al controlador oa la cola.

Sea cual sea el enfoque que haga ... la principal diferencia acerca de "un repositorio clásico" es que NUNCA está invocando el repositorio en el controlador. El controlador no "sabe cómo administrar entidades" (repositorio clásico) pero sí sabe "cómo administrar INTENCIONES DE HACER COSAS a las entidades" (manejador de comandos).

Luego es el controlador de comandos que, como su nombre lo dice, "maneja el comando" que vino de algún lugar (un controlador web html, un controlador de API, una línea de comandos, lo que sea) que se envió como una intención y es el CommandHandler (que pertenece al lado de escritura) que decide qué hacer con ese comando.

Por ejemplo, CommandHandler puede usar un repositorio para obtener el producto, establecer el estado y guardar (como en el ejemplo), pero también puede escribir en un registro de eventos, activar elementos para actualizar los lados de lectura, activar conectores externos o lo que sea.

La interfaz de usuario basada en tareas del usuario para calificar el producto y su controlador como AGNÓSTICO de "dónde" se coloca o almacena el sistema de estrellas / calificación. Imagine que diseña un sistema desde cero y la clase Product ya contiene el método RateProduct () como en el ejemplo. Bien.

Pero ... ¿qué pasa si tiene un sistema heredado con un enfoque "Antiguo" del Producto? En el "modelo" (es decir, Business Mind) no hay "calificación" del producto. En cambio, el encargado de marketing quiere "agregar" calificación al producto existente, pero toda la empresa está de acuerdo en que se trata de una "cosa externa". ¿Usaría el repositorio de productos? ¿O quizás otro almacenamiento auxiliar para que no "toque" el Producto y el Repositorio de productos que ya están funcionando y completamente probados?

Si usa comandos, no le importa al controlador de escritura. La web, la API y la CLI enviarán el "comando" al controlador de comandos (ya sea directamente, a través de la cola o mediante un bus de comandos que se enrutará a su vez al controlador oa una cola) y se olvidan. Luego, el manejador de comandos decidirá qué hacer con el "RateProductCommand" en un punto centralizado y pequeño de su código fuente, desacoplando la forma en que esto se maneja del código de la aplicación, ganando así la mantenibilidad.

Luego, el manejador decidirá si es adecuado utilizar ProductRepository o cualquier otro enfoque para almacenar "la calificación de un producto".

Entonces, para responder:

CommandHandler => no tiene nada que ver con las entidades. Maneja las "intenciones del usuario" (que en última instancia podría ser alterar entidades; por lo que lo más probable es que el controlador de comandos use un repositorio).
Repositorio => el almacenamiento real de una determinada entidad.

Espero poder ayudar.
Xavi.

Como mencionó @xmontero , ProductsCommandHandler y ProductRepository solo tienen relación "tiene". ProductsCommandHandler actúa como interfaz entre ProductApi / Controller y ProductRepository. Esta implementación ayuda a mantener los comandos juntos y separados de las consultas. En el enfoque tradicional, usamos directamente el repositorio del controlador manteniendo la ejecución de comandos y consultas juntos, por lo que no es necesaria ninguna otra interfaz entre ellos.

Ha aplicado el patrón de diseño CQRS en su aplicación, si mantiene los comandos separados de las consultas. Lo lleva en la dirección de obtener todos los beneficios de CQRS mencionados en los artículos.

¿Fue útil esta página
0 / 5 - 0 calificaciones