Arquitectura limpia en Angular 17

Daniel Herrera Sánchez
Bancolombia Tech
Published in
11 min readMar 1, 2024
Portada

Arquitectura limpia es uno de los paradigmas de desarrollo de software más mencionados en el mundo de la tecnología. ¿Te gustaría saber qué es?, ¿cómo implementarla? y ¿qué beneficios tiene? en este articulo te contaremos más.

Antes de comenzar, si sientes que ya conoces los conceptos básicos de arquitectura limpia, puedes continuar al siguiente capítulo, allí empezaremos a ver a profundidad Clean Architecture aplicado en Angular.

Arquitectura de Software

Es la parte del desarrollo de soluciones que permite estructurar proyectos y definir los lineamientos que tendremos que seguir en durante la implementación de los componentes de nuestra solución. Su principal objetivo es que el desarrollo sea fácil de implementar, operar y mantener.

Cuando logramos diseñar una buena arquitectura de software, a nuestra solución le será fácil adaptarse al cambio de forma oportuna sin alterar su estabilidad.

También, la arquitectura de nuestra solución debería buscar la independencia entre sus diferentes capas. Por ejemplo, si en la evolución de nuestra solución, debemos cambiar la base de datos, una API o cualquier otro componente de nuestro desarrollo; esto no debería afectar la capa de presentación. La arquitectura sólo deberá verse afectada cuando cambian de manera drástica las reglas del negocio.

Clean Architecture no es tu única alternativa

Antes de entrar en el detalle de esta forma de diseñar tus arquitecturas; te quiero comentar que: Clean Architecture no es una bala de plata; es decir, no estás obligado, ni es necesario aplicarla a todas tus soluciones. No obstante, todas tus soluciones deberían tener una buena arquitectura de software.

En el caso de Angular recomiendo analizar en cada escenario, cuál tipo de arquitectura se ajusta más a tus necesidades. Existen muchas que podemos utilizar como:

  • Arquitectura hexagonal (también conocida como puertos y adaptadores)
  • MVC
  • MVVM.

En otro artículo profundizaremos más sobre el tema de selección de arquitecturas.

Clean Architecture

Si traducimos en Traductor de Google: Clean Architecture, nos arroja lo siguiente:

100% real, no fake 🤯🤣

En efecto: Es un arquitectura de software diseñada por Robert C. Martin, que consiste en conjunto de capas bien definidas, las cuales están centradas en el dominio (el negocio y sus reglas). Puedes encontrar su libro acá: Clean Architecture: A Craftsman’s Guide to Software Structure and Design, First Edition.

Angular + Clean Architecture

El diagrama anterior es el más común a la hora de hablar de Arquitectura limpia. Este nos indica cómo, el Tío Bob (apodo de Robert), describe que debemos dividir nuestras soluciones en conjunto de capas. Estas capas parten desde el negocio como entidades y casos de uso, para luego llegar a la capa de presentación.

Es importante que sepas que el Tío Bob nos enseña en su libro algunos conceptos sujetos a su interpretación. Por lo tanto, podemos deducir que no existe una única forma de implementar Clean Architecture. Lo que sí podemos afirmar, es que busca la segregación de responsabilidades y centralizar nuestra solución en el dominio.

¿Qué implica centralizar una solución en el dominio?

El dominio es la capa definida por el negocio, es decir, no debe estar atada a ningún concepto técnico y dentro de ella vemos las entidades y los casos de uso.

Por ejemplo, pensemos en una solución como Spotify. El equipo de negocio debe definir lo que quiere, es decir, es el negocio quién decide cuáles son las entidades relevantes y sus casos de uso como:

  • Un usuario accederá a la app para escuchar música.
  • Deberá registrarse y autenticarse.
  • Podrá crear listas reproducción propias.
  • Tendrá la opción de darle me gusta a una canción.
  • Navegará a través de la aplicación buscando por artista, álbum, canción, o podcast, etc.

Además de las funcionalidades que se agregarán, el equipo de negocio también deberá definir las reglas de la aplicación, como si el mismo usuario puede ser artista y consumidor al mismo tiempo o deberá crear dos perfiles distintos.

Por lo tanto, tiene mucho sentido decir que debes centrar las soluciones en el dominio. Para una correcta definición de entidades y casos de uso se hace muy útil realizar sesiones de Domain Driven Design. Te dejo un artículo que entra en detalle en el tema (enlace).

Pero, ¿qué son las entidades y casos de uso?

Entidades

Son los objetos comerciales de la aplicación. Encapsulan las reglas más generales y de alto nivel. Son los menos propensos a cambiar cuando algo externo cambia. Por ejemplo, estos objetos no deberían verse afectados por un cambio en la navegación o la seguridad de la página. Ningún cambio operativo en ninguna aplicación en particular debería afectar la capa de entidad.

Casos de uso

El software en la capa de casos de uso contiene reglas comerciales específicas de la aplicación. Allí se encapsulan e implementan todos los casos de uso del sistema. Estos casos de uso organizan el flujo de datos hacia y desde las entidades, además, ordenan a esas entidades que usen sus reglas comerciales críticas para lograr los objetivos del caso de uso.

En esta capa no esperamos que los cambios afecten a las entidades y tampoco esperamos que esta capa se vea afectada por cambios en las externalidades como: la base de datos, la interfaz de usuario o
cualquiera de los marcos comunes.

La capa de casos de uso deberá estar aislada de tales preocupaciones. Por eso sería una mala práctica que un caso de uso se nombre: ApacheSMSSender; este debería llamarse: NotificationsSender.

Sin embargo, esperamos que los cambios en el funcionamiento de la aplicación sí afecten los casos de uso y, por lo tanto, su software. Si los detalles de un caso de uso cambian, algo de código en esta capa se verá afectado.

Teniendo esto en mente, nos queda faltando saber a qué capa pertenece el software destinado a conectarnos con las APIs y la capa de presentación. A partir de este momento comienza la interpretación. Con esto me refiero a que la forma de agrupar o desacoplar estos elementos puede ser diferente siempre y cuando no vulnere las definiciones establecidas.

Capa de Infraestructura

Contiene los siguientes elementos:

  • Driven adapters: serán los adaptadores los que nos permitirán conectarnos hacia el exterior, conforme a las necesidades que tengamos. En este tendremos todos nuestros repositorios de datos y escritura a estos.
  • Entry points: esta capa es cuando exponemos servicios. Para el mundo front-end no haremos uso de esta. Es común encontrarla en una solución back-end.
  • Helpers: es una capa dedicada a ayudar a sus hermanos (miembros de la capa de infraestructura) con transformaciones, operaciones, funciones útiles para la capa de infraestructura.

Capa de presentación

Implementa todo el software relacionado a los elementos visuales con los que el usuario final interactuará.

En algunas ocasiones cuando hablo del tema me suelen preguntar si esta bien tener una capa llamada Componentes para los elementos visuales reutilizables dentro de la aplicación. Para este tipo de componentes sugiero que mejor construyas tu sistema de diseño.

Implementemos arquitectura limpia en Angular

En esta ocasión explicaremos para Angular 17. Verás algunas notaciones nuevas que hacen parte de esta versión.

Utilizaremos la API que devuelve una lista de Album (enlace). Esta API devuelve los siguientes datos con la siguiente estructura:

Album API Service

Capa De Dominio

Recuerda que las entidades de arquitectura limpia deberían tomarse de una sesión de DDD pero no de una API. ¿Por qué? Esto se debe a que cuando aplicas arquitectura limpia estas desarrollando una solución centrada en el dominio (negocio).

Para entrar en más detalle imagina que eres desarrollador de una empresa de una aplicación que reproduce música, para el negocio es claro que es un álbum, pero ¿es claro que es userId e id? puede ser que a nivel técnico estemos asignando un id y un userId para asociar al creador. Pero estos son conceptos que el negocio no entendería a cabalidad, por lo que una identidad más parecida a la vista del negocio sería:

Para el negocio es claro estos atributos, título, genero, fecha de creado, artista. De forma que si nos tocara consultar varias fuentes de información para devolver la entidad Album definida por el negocio no deberíamos incluir estas entidades técnicas en la capa de dominio.

Sin embargo, como no contamos con múltiples APIs para este ejemplo, dejaremos por ahora la entidad relacionada a la API de Album.

Por tanto, creamos una carpeta llamada Domain la cual incluirá todos los elementos que definimos en la parte teórica a saber los modelos y los casos de uso.

Creación de carpeta principal Dominio y sus carpetas segundarias

Nuestro primer modelo agregado será para este escenario Album:

Pero ahora necesitamos adicionar un concepto muy interesante y es el gateway. Para explicar la importancia de esto, hablaremos de un ejemplo.

Supongamos que deseas hacer un conjunto residencial de casas y contratas un equipo de trabajadores. Tienes un conjunto de especialistas para lograr este fin. En este caso supongamos que tienes varios maestros de obra para el proyecto, el maestro de obra de obra debe entregar una casa que se vea igual a las demás sin importar que cambien los miembros de trabajo o los tiempos de entrega. De forma que a la final el proyecto mantiene una coherencia como conjunto.

El gateway define las funciones que se deben ejecutar relacionadas al dominio. De forma que definimos qué deseamos hacer, más no el cómo. Veamos un ejemplo:

Para este caso definimos las funciones que deseamos ejecutar relacionadas al Album. Pero no definimos cómo resolverlas. Es como si establecieras un contrato y comunicaras que esperas de alguien que implemente el rol de ser gateway.

Ahora, veamos el casos de uso, para el album:

Notemos que el caso de uso exige de entrada un gateway que lo podemos asociar a nuestro maestro de obra. Y el caso de uso espera que sin importar quien lo resuelva siempre obtenga el mismo resultado. Esto nos permite aislar los detalles. De forma que si se decide cambiar la forma de obtener los albums, por ejemplo ya no a través de un API sino que utilizando websockets, no resulta difícil el proceso de migración pues solo sería necesario cambiar en la capa de configuración quien va ser quien resuelva este gateway.

Capa de Infraestructura

En esta capa haremos una petición normal por la API establecida en el ejemplo. Para ejemplificar el cambio de backend crearemos dos APIs una con delay (un pequeño tiempo de espera) y otra sin el mismo. Estas APIs definidas irán dentro de la carpeta de infraestucture en la carpeta de driven-adapters.

Capa de Infraestructura

API sin delay:

API con delay:

Si notamos los servicios extienden del gateway transformándolos así en los especialistas que sin importar el cómo devolverán lo que se espera de ellos.

Capa de UI

Finalmente tenemos nuestra capa de presentación. Recuerda que en general si tienes suficiente capital de inversión sería bueno que implementaras un sistema de diseño para los componentes que se repetirán a lo largo de tu aplicación. Pero en caso de no ser posible recuerda que tienes la oportunidad de tener una carpeta con los componentes de tu sistema de diseño. Para este caso ponemos los componentes comunes dentro de la carpeta ds (design system) ¿Te gustaría un artículo de este tema aplicado a angular? déjalo en los comentarios.

En la capa de UI solo almacenaremos los elementos relacionados a presentar la información. Por tanto, no debemos depender de consumos http o consultas a bases de datos en esta capa solo dibujamos.

Ahora, supongamos que en una pantalla mostraremos la información del Album. En este escenario dependeríamos únicamente del caso de uso, como en el siguiente ejemplo:

Como vemos la capa de UI depende del dominio, de las reglas establecidas por el negocio. De esta forma conoce que quiere dibujar mas no sabe de donde se obtiene.

Por tanto, si cambiara algo en el backend pero no en el negocio no deberíamos afectar ni una linea de código en la capa de UI y tampoco de dominio.

Ahora surge la pregunta ¿Cómo todo se conecta si no dependen entre sí?

Capa de Configuración

Finalmente tenemos nuestra capa más externa es por decirlo así nuestro contratista. Decide quien va a ser el gateway que resuelva los casos de uso. También otros elementos tales como las rutas, variables de entorno, etc.

Por defecto esta carpeta no existe sino que esta en archivos externos, para darle algo más de orden creamos la carpeta de configuración.

Carpeta de configuración config.

El primer punto de entrada de configuración a nuestra capa aplicativa es el main.ts. Para este caso lo configuramos de la siguiente forma:

Como vez es igual al que viene por defecto generado por el CLI, pero actualizamos el componente raíz donde modificamos que fuera el MainComponent. También modificamos donde se aloja el appConfig a saber dentro de la carpeta de configuraciones.

En el appConfig es donde ocurre la magia, observemos:

En este archivo es donde proveemos en la línea 12 el gateway. Estamos estableciendo que cada vez que se necesite un AlbumGateway lo resolverá el AlbumApiService. Esto es genial pues si en un futuro cambiamos la forma de obtener la información del Album, solo sería necesario crear un nuevo servicio en la capa infraestructura y luego inyectarlo en el archivo de configuración. Por ejemplo si comentas la linea 12 y “descomentas” la 13 cambiarás un API por la otra 🤯 bastante simple ¿verdad?.

Conclusión

En este artículo, hemos explorado la arquitectura limpia y su aplicación en el desarrollo de aplicaciones web con Angular 17.

Los beneficios de la arquitectura limpia incluyen:

  • Mayor escalabilidad y mantenibilidad
  • Mayor facilidad para realizar pruebas
  • Mayor desacoplamiento entre las capas de la aplicación
  • Mayor claridad y organización del código

La implementación de la arquitectura limpia en Angular 17 requiere:

  • Definir las entidades y casos de uso del negocio
  • Implementar las capas de dominio, infraestructura y presentación
  • Usar gateways para desacoplar la capa de dominio de la capa de infraestructura
  • Configurar la aplicación en el archivo main.ts y definir los gateways en el archivo appConfig.ts

Espero que te haya gustado que dejes tus aplausos y estrellas en el repo ejemplo. De hecho antes que se me olvide 🤣 te adjunto el link al repositorio con el ejemplo completo:

Si tienes alguna inquietud no olvides dejarla en los comentarios.

--

--

Daniel Herrera Sánchez
Bancolombia Tech

Flutter,Dart@GoogleDevExpert💙 • 🔴Youtube Channel Weincode •👨🏻‍💻FlutterMedellin Community Lead • 🙅🏻‍♂️Angular Content creator • Speaker •GitHub: weincoder