El principio CQS (Command Query Separation)

Hoy les quiero hablar de estos principios que como arquitectos de software nos encantan, pues nos permite expandir mas nuestro vocabulario y aprender de pasada algunas nuevas técnicas, en este caso, les quiero hablar del principio Command Query Separation (CQS), que en español sería algo así como “Separación de consulta y comando”, aunque en lo particular me gusta utilizar el término en inglés,  pues en español suena espantoso.

Pero bueno, basta de tonterías y pasemos a lo realmente interesante. Este principio fue acuñado por Bertand Meyer en su libro “Object Oriented Software Construction”.  Este principio dice básicamente que, deberíamos de separar los métodos de una clase en dos categorías, una de consultas (query) y otra de comandos (command).

  • Query: son aquellos métodos que consulta información pero no modifican el estado de la aplicación.
  • Command: son los métodos que actualizan el estado de la aplicación pero no retornan un valor.

Básicamente, este principio nos ayuda a construir software más mantenible y testeable, pues deja muy en claro que los métodos de consulta (query) solo consultará el estado de la aplicación, y por ningún motivo lo cambiarán, lo que permite utilizar todos estos métodos con toda seguridad y sin la preocupación de que estos actualizarán el estado. Pero por otra parte,  los métodos de comando (command) solo actualizarán el estado de la aplicación.

Para comprender este principio, podríamos comprar este principio con los métodos get y set de una clase:

public class Product {
	private Long id;
	private String name;
	
	public Long getId(){
		return id;
	}
	
	public void setId(Long id){
		this.id = id;
	}
	
	public String getName(){
		return name;
	}
	
	public void setName(String name){
		this.name = name;
	}
}

Podrás observar claramente que los métodos get y set solo sirven para consultar el estado de la clase pero sin actualizarlo, ahora bien, si nos vamos a los métodos set, podrás ver que su único objetivo es actualizar el estado, pero no retornan ningún valor (void).

A pesar de que este principio es relativamente fácil de comprender, no pasa lo mismo a la hora de llevarlo a la práctica, pues en muchas ocasiones es necesario crear método que actualicen el estado y retornen un valor, por ejemplo, un servicio que guardar un Producto en la base de datos y nos regrese el ID generado:

public Long save(Product product) {
	Long id = dao.persist(product);
	return id;
}

Y bueno, se que estarás pensando, quizás se pueda hacer de otra forma, ¿pero te has puesto a pensar como es posible recuperar el ID generado si no lo hace justo en el momento de la inserción?

Ahora bien, esto no ocurre solo con un servicio que inserta en la base de datos, existen cientos de casos muy similares donde este principio no se cumple, un ejemplo de esto es el legendario método pop de una pila, el cual saca el primer elemento y lo retorna, o que tal el método next de un iterador en Java, el cual retorna el siguiente elemento de una colección pero incrementa el apuntador al siguiente elemento, o que pasa por ejemplo con servicios auditables, donde tenemos que guardar un registro para saber cada vez que un usuario intento leer un recursos, algo muy similar a cuando un usuario se intenta autenticar sin éxito en varias ocasiones, lo que daría como resultado el bloque del usuario, pero entonces, si el login es solo una consulta, ¿cómo podríamos actualizar el contado de logins falidos?

Dicho lo anterior, no quiero parecer fatalista y decir que este principio no sirve, al contrario, creo que es un excelente principio que deberíamos de utilizar donde sea posible, y quiero hace un especial énfasis en la palabra “posible”, pues este principio no es la panacea, ya que como hemos expuesto en este artículo, no siempre podrá ser posible cumplirlo y eso está bien, pero habrá otros casos donde sí.

A modo personal, me gustaría pensar que este principio nos deja una enseñanza, la cual yo interpreto de la siguiente forma, “busquemos en la medida de los posible separar los método que consultan de los que actualizan, de tal forma que los consumidor sepan con certeza que un método solo consultará el estado y los otros solo para actualizarlo”, como nota final, yo dejaría como opcional que un método de comando (command) no retorne datos, ya que en la práctica vemos que es muy normal que este tipo de método retorna algún resultado.

Finalmente, yo agregaría que los nombres de los métodos deberían de cumplir cierta nomenclatura que deje muy claro la intención del método, ya que al igual que tenemos los métodosget y set, podríamos utilizar prefijos claros como:

Para actualizar

  • set
  • delete
  • update
  • create
  • save
  • remove
  • change

Lo que nos darían nombres para métodos como: setName, updateOrder, deleteEmployee, removeUser, createCustomer, etc.

Para consultar:

  • get
  • find
  • retrieve
  • query
  • search
  • is

Estos nos darían nombres de método como: getName, findOrder, retrievePayment, queryUser, searchProduct, isActive, etc.

👆👆👆 ¿Te gustaría aprender más principios de diseño como este y convertirte en arquitecto de software? te invito a que veas mi libro de Introducción a la arquitectura de software, el mejor libro de arquitectura en Español.

Conclusiones

Como siempre, me gusta dar una conclusión de lo que pienso, aun que debe de quedar claro que es una apreciación personal, por lo que tu podrías pensar diferente y eso está bien, ya que es parte del debate que podríamos tener.

Pues bien, lo que yo pienso es que este es un patrón que nos invita a separar claramente los método en dos categorías, lo cual está super bien por que hace el código más entendible, mantenible y testeable, sin embargo, yo veo que es un principio que romperemos más de lo que nos gustaría, lo que nos puede hacer creer que estamos creando código con malas prácticas, sin embargo, y como hemos demostrado en este artículo, en la práctica es difícil lograr en muchos casos cumplir al pie de la letra este principio, por lo que me mi recomendación es, trata de cumplirlo cuando puedas, pero no dudes en romperlo cuando sea necesario.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *