Alexandru V. Simonescu

Software Craftsmanship and Android Development

Clean Architecture (Spanish)

Article written in spanish, as exported from my old spanish blog. All new published articles are in english, as is the main language of the blog.

Este artículo es una mera interpretación del original del de “Uncle Bob”, por tanto la autoría no es mía aunque haga alguna observación personal. Muchos términos he preferido no traducirlos por no tener ningún sentido en español.

Represents some layers that can be present in Clean Architecture defined by Uncle Bob.

En los últimos años hemos visto gran cantidad de ideas sobre arquitectura de sistemas. Algunas son:

Aunque todas estas arquitecturas pueden variar, algunos detalles son muy similares. Todos tienen el mismo objetivo, que es la separación de intereses o principios (separation of concerns). Todos los frameworks descritos cumplen esta separación dividiendo la aplicación en capas. Cada una de estas arquitecturas produce sistemas con las siguientes características:

  • Independiente de frameworks. La arquitectura no depende de la existencia de ninguna librería cuyas características nos aten a sus requisitos. Esto permite usar los frameworks como si fueran herramientas, en lugar de someter el sistema a las restricciones impuestas.
  • Testeable. La lógica de negocio se puede testear sin necesidad de la interfaz de usuario (UI), base de datos, servidor web u otras herramientas externas.
  • Independiente de la UI. La interfaz gráfica de usuario puede cambiar fácilmente sin cambiar el resto del sistema. Una UI web puede reemplazarse con una interfaz de consola, sin tener que cambiar la lógica de negocio.
  • Independiente de la base de datos. Puedes cambiar desde una base de datos en Oracle o SQL Server a MongoDB, BigTable, CouchDB o cualquier otra. Las reglas del negocio no están ligadas a la base de datos. Nota personal: razón por la que se están dejando de usar procedimientos almacenados o funciones almacenadas en el servidor de bases de datos.
  • Independiente de factores externos. En realidad tus reglas de negocio (lógica de negocio) no tienen constancia de nada que provenga del mundo exterior.

El diagrama de la imagen del principio del artículo es un intento de integrar todas estas arquitecturas en una sola idea entendible.

La regla de dependencia

Los círculos concéntricos representan diferentes áreas del software. En general, cuanto mas nos alejemos del núcleo mayor es el nivel del software. Los círculos exteriores son mecanismos mientras que los interiores son políticas. La regla primordial que hace que esta arquitectura funcione es la regla de dependencia. Esta regla dice que las dependencias a nivel de código fuente sólo pueden apuntar hacia dentro. Nada que se encuentre en un circulo interior puede saber algo sobre lo que hay en un círculo exterior. Particularmente, algo declarado en un círculo externo no puede ser mencionado desde el código situado en un círculo interno. Eso incluye funciones, clases, variables o cualquier otra entidad software. Por la misma razón, los formatos de datos usados en un círculo exterior no deberían usarse por un círculo interior, especialmente si esos formatos son generados por algún framework en un círculo situado al exterior (Nota personal: es cierto, algo confusa la frase..). No queremos que nada de un círculo exterior impacte en los círculos o niveles interiores.

Entidades

Las entidades encapsulan la lógica de negocio de la empresa (hablamos de software empresarial). Una entidad puede ser un objeto con métodos, o puede ser un conjunto de estructuras de datos y funciones. No importa mientras las entidades se puedan re/utilizar desde diferentes aplicaciones de la empresa. Si no tenemos una empresa, estaremos escribiendo una sola aplicación, entonces esas entidades son los objetos de negocio de solo esa aplicación y no de la empresa. Encapsulan la mayoría de la lógica general y de alto nivel. Los menos propensos a sufrir cambios debidos a un cambio externo. Por ejemplo, no se espera un cambio en las entidades habiendo modificado la navegación de una página o la seguridad. Ningún cambio operacional de una aplicación particular debería afectar a la capa de entidades.

Casos de uso

El software de esta capa contiene reglas de negocio específicas de la aplicación. Encapsula e implementa todos los casos de uso del sistema. Estos casos de uso dirigen el flujo de datos hacía y desde las entidades y hacen que las entidades usen su lógica de negocio empresarial para conseguir el objetivo del caso de uso. Los cambios en esta capa no deberían afectar a las entidades. Tampoco esperamos que cambios externos como en la base de datos, algún framework o la interfaz gráfica afecten a esta capa. Esta capa se encuentra aislada de ese tipo de principios (concerns). En cambio si esperamos que cambios en las operaciones de la aplicación afecten a los casos de uso y por tanto al software de esta capa. Si los detalles de un caso de uso cambian, entonces algo de código de esta capa cambiará indudablemente.

Interface Adapters (adaptadores de interfaz)

El software de esta capa es un conjunto de adaptadores que convierten datos desde el formato mas conveniente para el caso de uso y entidades al formato mas conveniente o aceptado por elementos externos como la base de datos o la web. Es esta capa, por ejemplo, la que contendrá toda la arquitectura MVC de una interfaz gráfica. Los presentadores, vistas y controladores pertenecen a esta capa. Los modelos son sólo estructuras que se pasan desde los controladores a los casos de uso y de vuelta desde los casos de uso a los presentadores (presentadores) y vistas. Del mismo modo, los datos son convertidos en esta capa, desde la forma de las entidades y casos de uso a la forma conveniente para cualquiera que sea el framework de persistencia de datos.

El código interior a esta capa/círculo no debería tener constancia de nada relacionado con la base de datos, por tanto toda sentencia SQL esta restringida a esta capa y en particular a partes de esta capa que tengan que ver con la base de datos. También en esta capa están los demás adaptadores encargados de convertir datos obtenidos de una fuente externa, como un servicio externo, al formato interno usado por los casos de uso y las entidades.

Frameworks y Drivers

El círculo o capa más externa se compone generalmente de frameworks y herramientas como la base de datos, el framework web, etc. Normalmente en esta capa sólo se escribe código que actúa de pegamento con los círculos o capas interiores. En esta capa es donde van todos los detalles. La web es un detalle. La base de datos es un detalle. Mantenemos estas cosas fuera del alcance donde no pueden hacer mucho daño.

¿Sólo cuatro círculos?

No, los círculos son esquemáticos. Puede pasar que necesites más de los cuatro ya expuestos. No hay ninguna regla que diga que siempre hay que tener los cuatro. De todas formas, la regla de dependencia hay que aplicarla siempre. Las dependencias a nivel de código solo apuntan hacia dentro, a capas o círculos interiores. A medida que profundizamos en la arquitectura, el nivel de abstracción aumenta. El círculo mas exterior tiene un nivel de detalle bajo. A medida que profundizamos el software se vuelve mas abstracto y encapsula políticas de nivel mas alto. El círculo mas interior es el mas general.

Crossing boundaries (cruzar límites/fronteras)

En el lado inferior derecho del diagrama del principio, hay un ejemplo de como se cruzan los límites del círculo. Muestra como los controladores y presentadores se comunican con los casos de uso del círculo siguiente. Nótese el control del flujo. Empieza en el controlador, atraviesa el caso de uso y finaliza en el presentador. Nótese también la dependencia a nivel de código fuente. Cada uno de ellos señala hacia dentro, hacia los casos de uso. Normalmente resolvemos esta aparente contradicción usando el principio de inversión de dependencias. En un lenguaje como Java, por ejemplo, desarrollaríamos interfaces y relaciones de herencia de tal manera que las dependencias de código fuente se oponen al flujo de control en tan sólo los puntos de derecho a través de la frontera. Consideremos por ejemplos, que los casos de uso necesitan llamar al presentador. Sin embargo, esta llamada no puede ser directa ya que no se cumpliría el principio de reglas de dependencias. Ningún nombre de un círculo exterior puede ser mencionado por un círculo interior. En ese caso tenemos que hacer el caso de uso llama a una interfaz en el círculo interno y hacer que el presentador implemente dicha interfaz en el círculo exterior. La misma técnica es usada para cruzar límites o fronteras en las arquitecturas. Aprovechamos el polimorfismo dinámico para crear dependencias de código que se opongan al flujo de control para cumplir la regla de dependencias sin importar en que dirección va el flujo.

Que datos cruzan los límites

Normalmente los datos que cruzan los límites son estructuras de datos simples. Puedes usar estructuras simples o DTO (Data Transfer Objects) si así lo deseas. O los datos pueden ser simplemente argumentos de una función. O puedes empaquetar todo en un hashmap, o construir un objeto. Lo importante es que las estructuras de datos aisladas y simples sean transportadas a través de los límites. No queremos hacer trampas y pasar entidades o filas de base de datos. No queremos que las estructuras de datos tengan alguna dependencia que haga quebrantar la regla de dependencias. Por ejemplo, muchas base de datos devuelven los datos en un forma propia como respuesta a una query (como un cursor). Podemos llamarlo RowStructure. No queremos pasar esa estructura como parámetro, ya que quebrantaría la regla de dependencias porque forzaría a un círculo interno saber algo sobre un círculo externo. Entonces, siempre que los datos tengan que cruzar límites se hará en el formato que mejor le convenga al círculo interior.

Conclusión

Aplicar estas sencillas reglas no es difícil y nos ahorrarán un montón de dolores de cabeza según avancemos. Separando el software en capas y aplicando la regla de dependencias, crearemos un sistema intrínsecamente testeable con todas las ventajas que implica. Cuando cualquiera de las partes del sistema se vuelve obsoleta, como la base de datos, o el framework web, puedes reemplazarlos sin mucho esfuerzo.

Header image by Garth Pratt.