Tengo que comprometer: DRY o Command-Query-Separation?

10

Recientemente estuve refactorizando un método que era tanto un comando como un método de consulta.

Después de separarlo en un método de un comando y un método de consulta, descubrí que ahora hay varios lugares en el código donde estoy llamando al comando y luego obtengo el valor de la consulta, lo que parece una violación del principio DRY.

Pero si tuviera que incluir ese código común en un método, ese método sería tanto comando como consulta. ¿Es esto aceptable?

Kris Welsh
fuente
De acuerdo, no sabía si la comunidad estaba de acuerdo, y no pude encontrar ninguna discusión sobre este tema.
Kris Welsh
Se llama más comúnmente CQRS google.com.au/…
Daniel Little
@DanielLittle: no, no lo es. CQS y CQRS son sujetos claramente diferentes. CQRS es un patrón arquitectónico mucho más complicado, mientras que CQS es más un patrón de diseño y mucho más fácil de comprender e implementar. Ver codebetter.com/gregyoung/2009/08/13/command-query-separation
Erik Funkenbusch
@Erik Funkenbusch Tienes razón
Daniel Little

Respuestas:

11

Siempre hay compensaciones a considerar entre principios de diseño en conflicto. La forma de resolverlo es mirar las razones subyacentes detrás de los principios. En este caso, ser incapaz de ejecutar una consulta sin ejecutar el comando es problemático, pero ser incapaz de ejecutar un comando sin ejecutar la consulta es generalmente inofensivo. Mientras haya una manera de ejecutar la consulta de forma independiente, no veo ninguna razón para no agregar el resultado de la consulta al comando, especialmente si se hace algo como esto:

QueryResult command()
{
   // do command stuff
   return query();
}
Karl Bielefeldt
fuente
4

No he oído hablar de Command-Query-Separation (CQS) antes, pero parece que se relacionaría con el Principio de responsabilidad única (SRP), que establece que una función / clase idealmente debería ser responsable de hacer una cosa y solo una cosa .

Si su código de comando es de 20 líneas de código y el código de consulta son otras 30 líneas y están todas en un cuerpo de función, claramente está violando SRP y asumiría también CQS y esas dos piezas de lógica deberían estar separadas entre sí .

Sin embargo, siguiendo su ejemplo hipotético, lo más probable es que cree un método contenedor que combine su comando y consulta para que DRY no se viole en numerosos lugares del código. Tampoco consideraría que esto es una violación de SRP (y tal vez CQS), porque el contenedor todavía tiene una sola responsabilidad: combinar el comando con una consulta y crear una abstracción de nivel superior que sea más fácil de consumir.

Creo que el método wrapper es una solución perfectamente aceptable y, para ilustrarlo, llevemos su ejemplo un paso más allá. ¿Qué pasaría si tuviera que ejecutar 2 consultas en lugar de 1 y luego realizar una acción de comando basada en eso? Entonces, sus 2 líneas de código serían 6 u 8. ¿Qué pasaría si hubiera alguna validación / verificación de datos entre una y otra, por lo que ahora tiene 15 líneas de código? ¿Se lo pensaría dos veces antes de crear un contenedor que haga todo eso, en lugar de rociar esas 15 líneas en varios archivos?

DXM
fuente
Creo que el "principio único" de un contenedor debería ser mantener los otros métodos que necesitan el comando y la consulta juntos SECOS.
Droogans
Google CQRS: google.com.au/…
Daniel Little
Si bien la solución de Karl a este problema es mejor, creo que su elaboración en funciones de envoltura más largas es un muy buen punto.
Kris Welsh el
-3

DRY es más importante, ya que resuelve una necesidad mucho más fundamental: evitar esfuerzos redundantes y malgastados. Esto es algo fundamental: uno no necesita ser un programador para comprenderlo.

CQS es una respuesta a la dificultad, en lenguajes sin soporte para los efectos de seguimiento, de comprender el código que se ejecuta tanto por sus resultados como por sus efectos. Sin embargo:

  1. La necesidad de ejecutar código para sus resultados no se puede evitar, porque esta es la base para componer programas grandes a partir de unidades pequeñas.

  2. Tampoco se puede evitar la necesidad de ejecutar código para sus efectos, porque, aparte de las matemáticas y la informática teórica, el valor de ejecutar un programa radica en lo que puede hacer por nosotros.

  3. La necesidad de causar efectos y producir resultados en el mismo código no se puede evitar, porque, en la práctica, necesitamos efectos y composición, no solo uno u otro.

¡La solución real al problema de que los efectos de seguimiento sean demasiado difíciles para los humanos sin ayuda es, por supuesto, que las computadoras nos ayuden a los humanos ! Algo similar puede decirse sobre el seguimiento de relaciones complejas entre valores de tiempo de ejecución (como la validez de los índices de matriz), para los cuales las excepciones y los contratos ejecutados en tiempo de ejecución constituyen soluciones (no).

En conclusión, las "soluciones" como CQS simplemente se interponen en el diseño de programas de acuerdo con principios sólidos basados ​​en la realidad. Ve por SECO.

pyon
fuente
A veces debe evitar el acoplamiento para disminuir la complejidad. Deberías echar un vistazo a CQRS.
Daniel Little, el
@Lavinski: La mejor herramienta para evitar la complejidad (no disminuirla, eso es inútil) es la abstracción, desacoplando la esencia genérica de los problemas que estamos resolviendo de los detalles particulares de las instancias de dichos problemas genéricos. En el mejor de los casos, las recetas mágicas (o "patrones de diseño", como oigo llamarlos) pueden evitar que cause demasiado daño cuando se equivoca su diseño, pero no pueden convertir un diseño incorrecto en el correcto.
pyon
@Lavinski: Con respecto a CQRS específicamente, la solución alternativa conceptualmente correcta es 1. comprender el modelo de datos (ninguna cantidad de capas de objetos puede eliminar la necesidad de esto), 2. codificar tantas propiedades de corrección como sea posible en el esquema de la base de datos. (Lamentablemente, los RDBMS más populares proporcionan un soporte bastante limitado para este último, sin mencionar los NoSQL, lo que hace que esto sea aún más incorrecto. Mi investigación actual está proporcionando una mejor solución para esto.)
pyon
CQRS funciona completamente en línea con el diseño impulsado por dominio. Le sugiero que investigue un poco. El dominio dentro de la aplicación debe exigir la corrección, no su almacén de datos.
Daniel Little, el
1
@ EduardoLeón: Si quiere demostrar que su diseño es correcto, intente escribir pruebas para su programa. Te puedo garantizar que tirar CQS solo obstaculizará tus esfuerzos en eso.
Stefan Billiet