4 Command-query separation

Hablemos de CQS. No, de CQ-R-S no, sino de C-Q-S: el principio Command Query Separation. Enunciado por Bertrand Meyer, reza más o menos así:

Cada método debería ser o bien un comando que ejecuta una acción o bien una pregunta que devuelve información.

Esta distinción es fundamental y puede ayudar muchísimo a tu paz de espíritu y a la predictibilidad y testeabilidad de tu código. Es el no mezclar churras con merinas de la orientación a objetos. Por supuesto, es una aplicación del principio de separación de intereses. Y en realidad es muy fácil de respetar.

Empecemos por los comandos:

Un comando (command) produce un efecto o cambio en el estado del sistema. Por ejemplo: crea un nuevo registro en una base de datos, o genera un archivo en algún sitio. Lo que haga falta. Pero no devuelve ninguna respuesta: void. Si no ha fallado es que ha ido bien. Si algo ha ido mal lo mejor es que lance una excepción.

– Oye, y eso ¿cómo se testea?

– Pues se me ocurren dos formas:

  • Si es posible comprobamos que se ha producido el cambio deseado en el sistema.
  • En el nivel unitario, usamos mock para verificar que se ha enviado el mensaje que produce el efecto.

– ¿Y si devuelve OK o true o ACK para indicar que ha ido todo bien?

– ¿Qué tiene de malo tirar una excepción? De otro modo tienes que esperar una respuesta y comprobarlo. Eso te lo da try/catch y es bastante más bonito.

Y no te confundes con las queries.

Las preguntas, o queries, por su parte devuelven una respuesta informando sobre el estado del sistema: ¿me das el producto con Id 1234? ¿Cuánto suman 2 y 3,45? ¿Cuál es el sentido del universo y todo lo demás?

El problema que tienen las queries es que, ya que estamos podríamos hacer algo, además de preguntar. Pues oye: NO. Porque ese hacer algo es producir un cambio en el sistema y si al hacer una pregunta cambias el estado: ¿cómo te puedes fiar de la respuesta?

Imagina un sistema de coche autónomo que al preguntar las rpm del motor también las cambia. Por ejemplo, las aumenta un 5%. ¿Qué te indica la respuesta de las rpm? ¿Las mide antes o después de del cambio? ¿Qué consecuencias tiene eso? Básicamente que no puedes confiar en la respuesta.

Por eso deben separarse las acciones que cambian el sistema (command) de las acciones que obtienen información sobre el sistema (query). Por cierto, testear las queries es bastante sencillo, ya que solo tenemos que verificar la respuesta que devuelven.

Si una query produce algún efecto en el sistema hablamos de side effects y es algo que debemos evitar. Por el buen funcionamiento del sistema y porque de este modo podemos testear con confianza.

Una clase puede tener tanto commands como queries siempre que respetemos el SRP, claro. El principio CQS se refiere únicamente a que un comando no devuelva respuestas: se ejecuta y confiamos en que irá bien; y a que una query no produzca side effects poniendo el sistema en estado indeterminado. Y nadie quiere eso.

Llevando eso a objetos-método, como pueden ser los Casos de Uso, tenemos que separar aquellos que son comandos de los que son queries. Y aplica lo mismo y por las mismas razones.

¿Y CQRS? CQRS son las siglas de Command Query Responsibility Segregation y es algo así como llevar CQS hasta las últimas consecuencias, separando los commands, que implican escrituras, y las queries, que implican lecturas, incluso al nivel de la infraestructura. Por ejemplo, tendrías una base de datos para escritura, pero leerías los datos de una o más réplicas. Pero CQRS es más un patrón que un principio.

En cualquier caso, la separación command-query es fundamental para construir sistemas confiables y fáciles de mantener. No hay nada peor que un side effect ocurriendo vaya usted a saber dónde.