Arquitecturas Hexagonales

Qué tal estaís coders? Hoy vamos a hablar de una de las arquitecturas software que más se han puesto de moda en los últimos años, las arquitecturas hexagonales, pero antes de empezar con estas, sería bueno definir, en primer lugar, que se entiende por una arquitectura software y porque son necesarias conocerlas para hacer desarrollos escalables y facilmente mantenibles.

Una arquitectura de software se refiere a la estructura fundamental y organización de un sistema de software. Define cómo los diferentes componentes de software se relacionan entre sí, cómo se comunican y cómo se organizan para lograr los objetivos del sistema.

La arquitectura de software proporciona una visión de alto nivel de un sistema, mostrando las principales abstracciones y componentes del software, así como las interacciones entre ellos. También define los principios y patrones de diseño que guían la construcción y evolución del sistema.

Una arquitectura de software puede incluir componentes como capas, módulos, servicios, interfaces, bases de datos, entre otros. Estos componentes trabajan juntos para proporcionar la funcionalidad requerida por el sistema y se organizan de manera que sean coherentes, eficientes, mantenibles y escalables.

Algunos objetivos comunes de una arquitectura de software incluyen:

  1. Separación de preocupaciones: Dividir el sistema en componentes independientes que se centren en tareas específicas, lo que facilita el desarrollo, el mantenimiento y la comprensión del sistema.
  2. Modularidad: Organizar el software en módulos cohesivos y acoplados de manera flexible, lo que permite cambios individuales en los componentes sin afectar al sistema en su conjunto.
  3. Reutilización: Diseñar componentes que puedan ser reutilizados en diferentes partes del sistema o en proyectos futuros, lo que aumenta la eficiencia del desarrollo y reduce el tiempo y los recursos requeridos.
  4. Escalabilidad: Permitir que el sistema crezca y se adapte a mayores volúmenes de datos, usuarios o demandas sin perder rendimiento ni funcionalidad.
  5. Mantenibilidad: Facilitar la identificación y corrección de problemas, así como la incorporación de nuevas funcionalidades o mejoras en el sistema a lo largo del tiempo.
  6. Fiabilidad y rendimiento: Diseñar el sistema para garantizar la estabilidad, la tolerancia a fallos y el rendimiento adecuado, evitando cuellos de botella y optimizando los recursos disponibles.

Las arquitecturas de software pueden variar ampliamente dependiendo del tipo de sistema, los requisitos del negocio y las tecnologías utilizadas. Algunos ejemplos comunes de arquitecturas de software incluyen la arquitectura de tres capas, la arquitectura orientada a servicios (SOA), la arquitectura basada en microservicios y la arquitectura hexagonal, entre otras.

Arquitectura Hexagonal

También conocida como arquitectura de puertos y adaptadores, es un patrón de diseño arquitectónico que propone una estructura modular y desacoplada para aplicaciones. Fue introducido por primera vez por Alistair Cockburn en 2005 y se ha vuelto popular en el desarrollo de software. En la arquitectura hexagonal, el núcleo de la aplicación se encuentra en el centro, representado por un hexágono, y está rodeado por diferentes capas y componentes. El objetivo principal de esta arquitectura es separar la lógica del negocio o dominio de los detalles de implementación y las preocupaciones técnicas.

La arquitectura hexagonal se basa en los siguientes conceptos clave, luego veremos un pequeño ejemplo de implementación práctica para que os quede más claro:

  1. Capa de dominio o negocio: Esta es la capa central y contiene la lógica de negocio de la aplicación. Aquí se definen los modelos, reglas y operaciones del dominio.
  2. Puertos: Los puertos son interfaces que definen cómo se pueden interactuar con el núcleo de la aplicación desde el exterior. Pueden ser interfaces o clases abstractas que representan puntos de entrada y salida para las operaciones del dominio.
  3. Adaptadores: Los adaptadores son implementaciones concretas de los puertos. Se encargan de adaptar las entradas y salidas de los puertos a los mecanismos o tecnologías específicas utilizadas en la aplicación. Los adaptadores pueden ser adaptadores de entrada (por ejemplo, para interactuar con una interfaz de usuario) o adaptadores de salida (por ejemplo, para interactuar con una base de datos o servicios externos).

La arquitectura hexagonal promueve el principio de inyección (o inversión en algunos libros) de dependencias, donde las dependencias están dirigidas hacia el núcleo de la aplicación, en lugar de depender directamente de detalles de implementación. Esto permite una mayor flexibilidad, mantenibilidad y pruebas unitarias, ya que los componentes se pueden reemplazar o adaptar fácilmente sin afectar la lógica del dominio.

Desventajas

No obstante, aunque estas ventajas son muy interesantes a tener en cuanta, no debemos olvidar las desventajas, que tenemos que poner en la otra parte de la balanza cuando tengamos que analizar que arquitectura implemento. Las desventajas más comunes son:

  1. Complejidad inicial: Implementar una arquitectura hexagonal puede requerir un esfuerzo adicional en comparación con enfoques más simples. La estructura modular y las capas adicionales pueden agregar complejidad al sistema, lo que podría aumentar el tiempo y los recursos necesarios para desarrollar la aplicación.
  2. Mayor cantidad de código: La arquitectura hexagonal puede resultar en una mayor cantidad de código debido a la necesidad de implementar adaptadores y puertos para separar y comunicar las diferentes capas. Esto puede dificultar la comprensión y el mantenimiento del código, especialmente en proyectos grandes.
  3. Curva de aprendizaje: Para los equipos que no están familiarizados con la arquitectura hexagonal, puede requerir tiempo y esfuerzo adicional para comprender y adoptar los principios y patrones asociados. Esto implica una curva de aprendizaje para los miembros del equipo y podría afectar la productividad inicialmente.
  4. Posible sobrediseño: En algunos casos, la arquitectura hexagonal puede resultar en un sobrediseño si se aplica a proyectos pequeños o simples. Si la complejidad y los beneficios adicionales no son justificados para el alcance y los requisitos del proyecto, podría resultar en una aplicación más compleja de lo necesario.
  5. Posible rendimiento reducido: La introducción de capas adicionales y la necesidad de comunicarse a través de adaptadores puede tener un impacto en el rendimiento de la aplicación. Si no se gestionan adecuadamente las comunicaciones y las transformaciones de datos entre capas, podría haber una sobrecarga en el procesamiento y la latencia.

Y esto hace que sea no importante sino crucial, evaluar cuidadosamente las necesidades del proyecto y considerar las ventajas y desventajas de la arquitectura hexagonal antes de decidir su implementación. En algunos casos, puede ser beneficioso para proyectos complejos y en constante evolución, mientras que en otros casos más simples, podría no justificar el esfuerzo adicional requerido. Mi recomendación siempre es aplicar el principio KISS (Keep It Simple Stupid) siempre que se pueda y no hacer sobre ingeniería, algo que en muchas ocasiones, nos encanta a los ingenieros software.

Ejemplo

Veamos ahora con un pequeño ejemplo, como se implementa esta arquitectura, voy a usar Javascript porque me parece que es el lenguaje más simple para que todo el mundo pueda entenderlo, y a partir de aquí, cada uno que lo adapte como quiera.

// Capa de Dominio (Core) 
class DomainUserRepository { 
    constructor(database) { 
        this.database = database; 
    } 

    getUserById(userId) { 
        // Lógica para obtener un usuario por su ID desde la base de datos 
        return this.database.query(`SELECT * FROM users WHERE id = ${userId}`); 
    } 

    saveUser(user) { 
        // Lógica para guardar un usuario en la base de datos 
        return this.database.query(`INSERT INTO users VALUES (${user.id}, '${user.name}')`); 
    } 
}

La clase DomainUserRepository representa la capa de dominio o negocio y se encarga de interactuar con la base de datos para obtener y guardar usuarios.

// Adaptadores de entrada (Primary Ports) 
class UserAPI { 
    constructor(userRepository) { 
        this.userRepository = userRepository; 
    } 

    getUser(request, response) { 
        const userId = request.params.id; 
        const user = this.userRepository.getUserById(userId); 
        response.send(user); 
    } 

    saveUser(request, response) { 
        const userData = request.body; 
        const user = { id: userData.id, name: userData.name }; 
        this.userRepository.saveUser(user); 
        response.send('User saved successfully'); 
    } 
}

El UserAPI actúa como un adaptador de entrada o Primary Port. Recibe las solicitudes HTTP y utiliza el DomainUserRepository para procesarlas y enviar las respuestas correspondientes.

// Adaptadores de salida (Secondary Adapters) 
class Database { 
    query(sql) { 
        // Lógica para ejecutar la consulta SQL en la base de datos 
        console.log(`Executing query: ${sql}`); 
        // ... código para ejecutar la consulta y devolver los resultados 
    } 
}

El adaptador de salida o Secondary Adapter, representado por la clase Database, se encarga de la comunicación con la base de datos, ejecutando consultas y devolviendo los resultados.

Finalmente:

// Configuración e inicialización de los componentes 
const database = new Database(); 
const userRepository = new DomainUserRepository(database); 
const userAPI = new UserAPI(userRepository); 

// Utilización del UserAPI desde la interfaz de usuario o enrutador 
app.get('/users/:id', (request, response) => { userAPI.getUser(request, response); }); app.post('/users', (request, response) => { userAPI.saveUser(request, response); });

La configuración e inicialización de los componentes se realiza creando una instancia de Database, DomainUserRepository y UserAPI, y se inyecta el DomainUserRepository en el UserAPI para establecer la dependencia y por último, se utiliza el UserAPI desde la interfaz de usuario o enrutador para manejar las solicitudes de obtener y guardar usuarios.

Por hoy nada más, confío en que os haya resultado de interés.

Más sobre Arquitectura de desarrollo en mis otras entradas:

Arquitecturas Hexagonales

Deja una respuesta

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

Scroll hacia arriba