DEV Community

Miguel Ángel Sánchez Chordi
Miguel Ángel Sánchez Chordi

Posted on • Originally published at Medium on

Patrón Command

Siguiendo con los posts sobre sobre patrones, hoy vamos a ver un patrón comportamental: el patrón Command.

Recordemos brevemente que el propósito de este tipo de comandos es definir la forma en que los objetos interactúan entre ellos. Dicho esto, el propósito en concreto de este patrón es:

Encapsular una petición como un objeto, de modo que puedan parametrizarse otros objetos con distintas peticiones o colas de peticiones.

Básicamente, queremos ejecutar una acción (pongamos por ejemplo registrar un usuario), y lo que hacemos es crear un comando con la información necesaria para que el manejador pueda realizar esta tarea.

Los actores principales en este patrón son:

  • Cliente/Invoker. Es la clase que inicia todo, en el ejemplo anterior de registrar un usuario, bien podría ser un controlador asociado a una ruta POST que toma los valores de un formulario para registrar a un nuevo usuario.
  • Command. Es la clase que encapsula toda la información necesaria para ejecutar la acción. Son simples DTOs en la mayoría de casos, aunque pueden contener otros objetos dentro. Es muy importante, como veremos más adelante, que sean fácilmente serializables. En nuestro ejemplo, esto sería una clase que contenga los datos del usuario a registrar, por ejemplo email y password.
  • CommandHandler/Handler. Es la clase que contiene la lógica a ejecutar, puede tener dependencias externas, como servicios de mensajería, conexiones a base de datos, servicios para escribir en logs… etc. Es importante que esta clase ejecute una y solo una acción, ya que en caso contrario resultaría muy confusa su nomenclatura.

Por convención, todos los handlers deberían de tener un método conocido para ejecutar los comandos, como: handle, execute, do… o en el caso de PHP usar el magic method __invoke.

El definir una interfaz común para todos los comandos se complica si queremos sacar partido del type hinting para asegurar que nuestro handler solo ejecute comandos del tipo adecuado. Una opción es que todos los comandos implementen una interfaz Command y que todos los manejadores implementen una interfaz CommandHandler, con un método execute(Command $command o handle(Command $command) y dentro del mismo ejecutar la comprobación de que es la clase adecuada.

Personalmente recomiendo el uso de __invoke, ya que no exponemos método alguno para su ejecución, más que la propia clase. Si no estáis familiarizados con el método __invoke os recomiendo echar un ojo a la documentación de PHP.

Ejemplo de implementación

Una implementación del ejemplo anterior podría ser:

Ventajas

El uso de este patrón nos reporta numerosas ventajas.

  • Bajo acoplamiento del código. Si vemos nuestro ejemplo del CommandHandler, hace uso inyección de dependencias y cumple con todos los principios SOLID. Si el día de mañana queremos cambiar de motor de base de datos, solo hay que implementar un nuevo servicio de repositorio que implemente la interfaz UserRepository e inyectárselo en lugar del actual.
  • Código fácilmente ampliable. Si a nuestros manejadores de comandos les inyectamos un despachador de eventos, estos pueden despachar eventos customizados y ampliar su funcionalidad sin modificar su contenido
  • Reutilización. El CommandHandler podemos reutilizarlo en nuevos controladores, en comandos de consola o cualquier otro punto de entrada a nuestra aplicación sin ningún problema.
  • Escalabilidad. Existen librerías como Tactician que implementan un bus de comandos. Esto es una especie de despachador de eventos, pero que en su lugar despacha comandos. Estos comandos pueden ir a parar a una cola de mensajes tipo RabbitMQ (de ahí la importancia de que sea fácilmente serializable el comando) y ser ejecutados asíncronamente por consumers en otros lenguajes más eficientes para su cometido.
  • Testeabilidad. Los CommandHandlers son muy fácilmente testeables: podemos crear mocks de sus dependencias, y además solo realizan una acción, con lo que es fácil obtener porcentajes de cobertura verdaderamente altos. Los Commands son simples DTOs o POPOs, con lo que no requieren test.

Casos de uso reales del patrón Command

  • La arquitectura CQRS, que aboga por la separación de los contextos de lectura y escritura en nuestra aplicación, usualmente hace uso de buses de comandos, de forma que estos puedan ejecutarse síncrona o asíncronamente según requiera el caso de uso.
  • Las librerías de CommandBus, como Tactician o Broadway hacen uso del patrón.
  • Las aplicaciones de escritorio basadas en ventanas hacen uso de una variante del patrón command, que permite además que las operaciones puedan deshacerse (el clásico Ctrl+Z).

Conclusión

El patrón Command es una herramienta muy potente, que nos permite crear código muy limpio y testable, su implementación básica es muy sencilla, y el hacer uso de un CommandBus como Tactician es muy sencillo también.

A mi personalmente es un patrón que me encaja bastante en casi todos los proyectos web, no añade un sobrecoste de código excesivo, y la testeabilidad y reutilización del código me compensa bastante. Está claro que no todos los proyectos requieren del uso de colas de mensajería, ni de ejecutar comandos de forma asíncrona, pero una implementación sencilla como la vista en el ejemplo encaja bien en proyectos de cualquier tamaño.


Top comments (0)