Action-Domain-Responder
Tags: designpatterns, actiondomainrespon, mvc, solid
Published: 2019-07-18 10:29:27

---
title: Action-Domain-Responder
published: true
tags: design-patterns,action-domain-respon,mvc,solid
canonical_url: https://medium.com/all-you-need-is-clean-code/action-domain-responder-efc4435d0e19
---

![](https://cdn-images-1.medium.com/max/1024/1*4H5qt_83vt8nACqghNtOtg.jpeg)

### Introducción

Siempre que he estado en alguna formación sobre SOLID, o alguna conversación entre compañeros sobre el tema, una cuestión recurrente siempre es: ¿_Y los controladores? ¿Cómo haces para que un controlador siga_ [_el SRP_](https://medium.com/all-you-need-is-clean-code/principio-de-%C3%BAnica-responsabilidad-srp-c67c3626ff47)_?_

Recordemos que el SRP es el **principio de única responsabilidad** según el cual, una clase solo de de tener **un único motivo de cambio** o lo que es lo mismo: **una única responsabilidad**.

Un controlador por su parte, en una arquitectura MVC, es una capa intermedia entre la vista y el modelo, que se encarga de obtener y transformar los datos del modelo en una respuesta válida para las vistas.

En frameworks web como Symfony o Laravel, los controladores son los que definen las rutas que van a estar disponibles para la aplicación, actuando como punto de entrada a través del navegador, obteniendo y manipulando los datos del modelo y pasándoselos a la vista.

El problema con esta aproximación es que los controladores tienden a volverse inmanejables por varios motivos:

- Los métodos que resuelven cada ruta acaban conteniendo el caso de uso en sí, es decir, la lógica que hay detrás de cada acción.
- Según como decidamos organizar nuestras rutas, los controladores tienden a manejar grandes cantidades de rutas, agrupando varias en una misma clase.

Estos dos motivos, a su vez son los que rompen con el SRP: **cada ruta que maneja un controlador es un motivo de cambio.**

### Adios MVC, bienvenido ADR

#### MVC

MVC es un patrón de arquitectura de software comúnmente usado para el desarrollo de aplicaciones con interfaz de usuario. Tuvo un gran auge en la década de los 90 y a principios de 2000.

En este patrón intervienen 3 actores:

- **Modelo:** Es el principal componente, define y maneja los datos de la aplicación. Concentra la lógica de negocio.
- **Vista:** Es la parte visual de la aplicación, la representación final de los datos o de las acciones realizadas de cara al usuario
- **Controlador:** Es el canal de comunicación entre la vista y el modelo. La vista le puede pedir información al model a través del controlador, y este se la servirá de nuevo a través del controlador.

#### MVC y Frameworks web actuales

Con el auge de las aplicaciones web, este modelo se replicó también para este tipo de aplicaciones, pero nunca ha llegado a encajar bien del todo, ya que está concebido para representar pequeños componentes dentro de aplicaciones de escritorio.

Si has trabajado con Symfony [sabrás que no es un framework MVC](http://fabien.potencier.org/what-is-symfony2.html), del mismo modo que [no lo es Laravel](https://twitter.com/taylorotwell/status/634384068990267392?ref_src=twsrc%5Etfw&ref_url=https%3A%2F%2Fstyde.net%2Fporque-laravel-no-es-mvc-y-tu-deberias-olvidarte-de-mvc%2F).

Symfony, por ejemplo, utiliza el paradigma Request/Response, cuyo flujo podemos ver en la ilustración siguiente:

![](https://cdn-images-1.medium.com/max/690/1*dv9t07ROpLoLPXXHX6HVEQ.png)<figcaption>Request-Response Flow</figcaption>

A pesar de todo esto, la figura del controlador apenas ha variado en todos estos paradigmas, y uno de los mayores problemas es que el controlador acaba convirtiéndose en un cajón de sastre, con buena parte de la lógica implementada en ellos y otra parte en los modelos.

#### Controladores en los actuales frameworks web

Este es el aspecto que podría tener un controlador de un proyecto basado en Symfony o Laravel:

{% gist https://gist.github.com/mangelsnc/803c196e045c02c30792b1d8e81e1bd5 %}

Como se puede observar, esta misma clase está gestionando 4 acciones diferentes:

- Listar todos los posts
- Mostrar un post en concreto
- Añadir un comentario
- Hacer una búsqueda

No voy a profundizar en el tema de las anotaciones, esto es solo un ejemplo sacado de un repositorio público de Symfony, pero siempre es mejor evitarlas para evitar el acoplamiento. Lo que realmente quiero que observes es que el controlador tiene nada menos que 4 motivos de cambio, y eso no es nada bueno.

Esta forma de organizar las rutas en los controladores se ha extendido mucho, hasta el punto de encontrarte con controladores que superan el millar de lineas, y eso es mucho peor.

Hace poco vimos como extraer la lógica de nuestra aplicación y hacerla reusable mediante el uso del [**Patrón Command**](https://dev.to/mangelsnc/patron-command-36ih-temp-slug-7092538), y ese es un primer paso para que nuestros controladores adelgacen, pero el problema con el SRP sigue ahí.

### Action-Domain-Responder (ADR)

El patrón ADR es un refinamiento del patrón MVC, propuesto por [Paul M. Jones](https://github.com/pmjones/adr), que encaja mejor para aplicaciones web allí donde más falla el MVC.

El patrón emula la comunicación HTTP (request-response) de una forma más eficiente. Sus actores principales son:

- **Action:** Es el equivalente al controlador de MVC. Toma el _input_ de la request y la utiliza para interactuar con el _dominio_, pasándole a un único _responder_ el _output_ resultante.
- **Domain:** Equiparable al modelo de MVC. Interactúa con la base de datos y manipula la información, es de nuevo quien contiene la lógica de negocio.
- **Responder:** Representa lo mismo que las vistas de MVC. Es quien muestra el resultado de la acción.

Aunque similares, tienen sus diferencias, y la más importante está en el par _Action/Controller_.

Mientras que en MVC un controlador contiene varios métodos que se corresponden con diferentes acciones (o rutas), en ADR un _Action_ es una clase auto-invocable (implementa el método \_\_invoke), no expone más métodos, así que cada nueva _Action_ que queramos modelar implica crear una nueva clase.

Desde la versión 2.7, Symfony nos da la opción de definir nuestros controladores como servicios, lo cual facilita esta implementación.

Veamos el ejemplo anterior refactorizado para usar ADR:

{% gist https://gist.github.com/mangelsnc/fa8356ce7c3ce98b63bac6cd14121d15 %}

La definición de los controladores como servicios es opcional a partir de la versión 3.3.

### Conclusión

Como se puede observar, la separación que aporta este nuevo paradigma es bienvenida, ya que nuestros controladores han quedado reducidos a simples _request handlers_, que manejan una única petición.

#### Ventajas

- **Mayor testabilidad.** Ahora los controladores son servicios, podemos inyectar el request que queramos y testear todas las posibles respuestas. Al solo tener un método nuestros tests van a quedar muy limpios.
- **Mayor organización.** La organización ahora queda muy estructurada, siendo fácil localizar el código que maneja cada ruta.
- **Facilidad para habilitar/deshabilitar rutas.** Si alguna vez queremos deshabilitar una ruta, es tan sencillo como eliminar en el archivo de _routing_ la entrada adecuada.
- **Ahora los controladores cumplen con el SRP.**

#### Update 4/6/2018

Recientemente tuve la oportunidad de presentar una charla sobre ADR en el #VLCTechFest de Valencia, dejo el video y las slides de la charla:

{% youtube 6hP6md1eaA0 %}

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.slideshare.net%2Fslideshow%2Fembed_code%2Fkey%2FDrGNfLN31ITtAG&amp;url=https%3A%2F%2Fwww.slideshare.net%2Fslaimer%2Fvlctechfest-simplificando-controladores-una-introduccin-a-actiondomain-responder&amp;image=https%3A%2F%2Fcdn.slidesharecdn.com%2Fss_thumbnails%2Fdttyiacs2e3t5b3zbabm-signature-9fe46bcb49efe2d99f98c8d8c8936f047b3fc97a43faaa440305731ad428fa84-poli-180603102424-thumbnail-4.jpg%3Fcb%3D1528021789&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=slideshare" width="600" height="500" frameborder="0" scrolling="no"><a href="https://medium.com/media/0fd0bad0527b9b5b921b79522cd7f22e/href">https://medium.com/media/0fd0bad0527b9b5b921b79522cd7f22e/href</a></iframe>
* * *