DEV Community

Cover image for La Arquitectura Hexagonal: un enfoque para desarrollar aplicaciones robustas en React, Angular y Vue con ejemplos de código
Dennys José Márquez Reyes
Dennys José Márquez Reyes

Posted on • Edited on

La Arquitectura Hexagonal: un enfoque para desarrollar aplicaciones robustas en React, Angular y Vue con ejemplos de código

Introducción

¡Hola! Si estás interesado en aprender más sobre la arquitectura hexagonal, estás en el lugar correcto.

🤜🤛🤓

Te mostraré cómo esta arquitectura puede ayudarte a desarrollar aplicaciones más escalables, robustas y fáciles de mantener.

Te explicaré esto con proyectos de React, React Native, Angular y Vue, utilizando ejemplos concretos de código para ilustrar cada paso y además te dejo una lista de consejos generales útiles para la implementación de esta arquitectura.

La arquitectura hexagonal es un patrón de arquitectura de software que se enfoca en separar la lógica del negocio de la interfaz de usuario mediante el uso de interfaces y puertos, esto permite que el núcleo sea fácilmente testeable y reutilizable.

Aislar la lógica de negocio de las dependencias externas es su objetivo, como la interfaz de usuario, las bases de datos y las llamadas a la red, para facilitar el mantenimiento y la escalabilidad de la aplicación.

El objetivo principal es permitir que los cambios en una parte de la aplicación no afecten a otras partes de la aplicación, lo que facilita el desarrollo y el mantenimiento a largo plazo.

En cuanto a la implementación de la arquitectura hexagonal en React, Angular o Vue, puedes crear un módulo o paquete que contenga el núcleo de tu aplicación y luego importarlo en los componentes de interfaz de usuario.

Con esto se busca es que las dependencias vayan de lo más alto a lo más bajo, ese es el enfoque a tener para todo, al importar la lógica del núcleo (negocio) en un componente, se busca que el componente dependa del núcleo, no el núcleo del componente; así sucesivamente de arriba hacia abajo, no de abajo hacia arriba mantén este enfoque en tu mente cuando construyas software.

También pueden utilizar patrones de inyección de dependencias, para que se aseguren de que el núcleo de sus aplicaciones no dependa directamente de las dependencias externas.

COMENCEMOS ...


/

La arquitectura hexagonal se divide en tres capas

.

Los puertos y los adaptadores aseguran que la capa de dominio no tenga dependencias directas con los sistemas externos.

Core ó (Núcleo, Dominio, lógica de negocio):

Es el núcleo o el corazón del sistema, donde se encuentra la lógica de negocio. Esta capa contiene la lógica y el estado de la aplicación, y es independiente de los puertos y adaptadores. El core o dominio es el encargado de manejar las acciones del usuario y proporcionar la información necesaria para mostrar en la interfaz. Por ejemplo, en un sistema de gestión de tareas, el core o dominio podría contener las funciones para añadir, editar y eliminar tareas y las reglas de negocio para manejar las fechas límite y la prioridad de las tareas.

Puertos:

los puertos son las interfaces que describen las operaciones que se pueden realizar con los sistemas externos. Los puertos son los puntos de contacto entre la capa de dominio y la capa de infraestructura, y describen las operaciones que los adaptadores deben ser capaces de realizar.

Que es infraestructura

La capa de infraestructura se refiere a los componentes de una aplicación que se encargan de las tareas de soporte, como la conexión a bases de datos, servicios web, envío de correo electrónico, etc. En la arquitectura hexagonal, la capa de infraestructura es donde se encuentran los adaptadores que se comunican directamente con los sistemas externos.

Los puertos son independientes de la implementación y son utilizados por la capa de dominio para interactuar con los sistemas externos, pero no tienen conocimiento de cómo se lleva a cabo esa operación. Los puertos sólo se preocupan por describir qué es lo que se quiere hacer, no cómo se hace.

Por ejemplo, un puerto podría describir una operación para recuperar un objeto de una base de datos, pero no especificar cómo se conecta a la base de datos o cómo se ejecuta la consulta. Eso es responsabilidad del adaptador correspondiente.

Los puertos también ayudan a aislar la lógica de negocio de la lógica de infraestructura, lo que facilita el mantenimiento, escalabilidad y cambios futuros en la aplicación.

Esto significa que la capa de dominio puede interactuar con los sistemas externos a través de los puertos sin tener que preocuparse por los detalles de cómo se conecta y se comunica con esos sistemas. Si se desea cambiar o reemplazar un sistema externo, sólo se tiene que modificar o reescribir el adaptador correspondiente, sin afectar al código de la capa de dominio ni al resto de la aplicación.

Además, esto permite que la aplicaciones sea más escalables, ya que se pueden agregar nuevos adaptadores para sistemas externos adicionales sin afectar al resto de la aplicación, los puertos son independientes de la implementación.

Esto hace que los puertos sean flexibles y adaptables para trabajar con distintos sistemas externos, lo que aumenta la escalabilidad de la aplicación y facilita el proceso de actualizar o agregar nuevos sistemas

Adaptadores:

Los adaptadores son responsables de conectarse y comunicarse con los sistemas externos y de traducir las solicitudes y respuestas entre el formato esperado por el puerto y el formato específico del sistema externo.

Por ejemplo, puede haber un adaptador para MongoDB y otro para MySQL, cada uno con su propia forma de extraer y manipular la data, pero el formato en el que se presenta al puerto es el mismo, esto permite que el código de la capa de dominio no tenga que preocuparse por los detalles de cómo se conecta y se comunica con los sistemas externos.

De esta manera, si se desea cambiar o reemplazar un sistema externo, sólo se tiene que modificar o reescribir el adaptador correspondiente, sin afectar al código de la capa de dominio ni al resto de la aplicación.


La aplicación de la arquitectura hexagonal en diferentes proyectos puede variar dependiendo de las necesidades específicas del proyecto y la plataforma elegida.

En este caso, vamos a analizar cómo se implementa esta estructura en proyectos de React, React Native, Vue y Angular, para comprender mejor cómo se adapta a cada uno de ellos.


/

A continuación …

.

Vamos a analizar cómo se implementa esta arquitectura en proyectos de (React, React Native, Vue y Angular), para comprender mejor cómo se adapta a cada uno de ellos.

En esta serie de ejemplos que les doy, ilustro cómo funciona la arquitectura hexagonal mediante la conexión a diferentes sistemas externos, como bases de datos, entre otros, utilizando los adaptadores.

Les muestro cómo los puertos actúan como interfases que describen las operaciones que se pueden realizar con estos sistemas externos, y cómo la capa de dominio con sus entidades y casos de uso interactúan con los puertos, para realizar operaciones con estos sistemas externos, asi lo comprenderan mejor.


Espero hacerme entender bien y así poderlos Ayudar con esto, estaré atento a sus comentarios, para posibles mejoras de esta, guía. 🤜🤛🤓


La Arquitectura Hexagonal en React

/
Estructura de carpetas:
/

src/
    adapters/
        DatabaseAdapter.js
        EmailAdapter.js
        ...
    ports/
        DatabasePort.js
        EmailPort.js
        ...
    domain/
        entities/
        usecases/
    pages/
        ...
    ...

Enter fullscreen mode Exit fullscreen mode

/
Archivos:
/

src/adapters/DatabaseAdapter.js:


import mongoose from 'mongoose';

const DatabaseAdapter = {
    async findOne(model, query) {
        const result = await model.findOne(query);
        return result;
    },
    async save(model, data) {
        const savedData = new model(data);
        await savedData.save();
        return savedData;
    }
    // ...
}

export default DatabaseAdapter;

Enter fullscreen mode Exit fullscreen mode

src/ports/DatabasePort.js:


import DatabaseAdapter from '../adapters/DatabaseAdapter';

export const DatabasePort = {
  findOne: (model, query) => DatabaseAdapter.findOne(model, query),
  save: (model, data) => DatabaseAdapter.save(model, data),
  // ...
};
Enter fullscreen mode Exit fullscreen mode

src/domain/usecases/CreateUser.js:


import { DatabasePort } from '../ports';
import { User } from '../entities';

export const createUser = async (data) => {
    try {
        const user = new User(data);
        await DatabasePort.save(User, user);
        return user;
    } catch (error) {
        throw new Error(error);
    }
}

Enter fullscreen mode Exit fullscreen mode

src/pages/CreateUser.js:


import { createUser } from '../usecases';
import { DatabaseAdapter } from '../adapters';

const CreateUser = () => {
  const handleSubmit = async (data) => {
    try {
      const user = await createUser(data);
      // hacer algo con el usuario creado
    } catch (error) {
      // manejar el error
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Nombre:
        <input type="text" name="name" required />
      </label>
      <br />
      <label>
        Correo electrónico:
        <input type="email" name="email" required />
      </label>
      <br />
      <label>
        Contraseña:
        <input type="password" name="password" required />
      </label>
      <br />
      <button type="submit">Crear usuario</button>
    </form>
  );
};

export default CreateUser;

Enter fullscreen mode Exit fullscreen mode

En este ejemplo, se utiliza la funcionalidad de los adaptadores para conectarse con sistemas externos, como una base de datos MongoDB, a través de la librería mongoose, mientras que los puertos actúan como una interfaz que describe las operaciones que se pueden realizar con ese sistema externo.

La capa de dominio contiene entidades y use cases, los cuales interactúan con los puertos para realizar operaciones con los sistemas externos. Los componentes de React utilizan las funcionalidades de los use cases para interactuar con la capa de infraestructura y mostrar la interfaz de usuario.


La Arquitectura Hexagonal en React Native

/
Estructura de carpetas:
/


src/
    adapters/
        database-adapter.js
        email-adapter.js
        ...
    ports/
        database-port.js
        email-port.js
        ...
    domain/
        entities/
        usecases/
    views/
        ...
    ...

Enter fullscreen mode Exit fullscreen mode

/
Archivos:
/

src/adapters/database-adapter.js:


import axios from 'axios';

const DatabaseAdapter = {
    async findOne(url, query) {
        const result = await axios.get(url, { params: query });
        return result.data;
    },
    async save(url, data) {
        const result = await axios.post(url, data);
        return result.data;
    }
    // ...
}

export default DatabaseAdapter;

Enter fullscreen mode Exit fullscreen mode

src/ports/database-port.js:

export const DatabasePort = {
    findOne: (url, query) => {},
    save: (url, data) => {},
    // ...
}

Enter fullscreen mode Exit fullscreen mode

src/domain/usecases/create-user.js:


import { DatabasePort } from '../ports';
import { User } from '../entities';

export const createUser = async (data) => {
    try {
        const user = new User(data);
        const savedUser = await DatabasePort.save('https://api.example.com/users', user);
        return savedUser;
    } catch (error) {
        throw new Error(error);
    }
}


Enter fullscreen mode Exit fullscreen mode

src/views/CreateUser.js:

import React, { useState } from 'react';
import { View, TextInput, Button } from 'react-native';
import { createUser } from '../usecases/create-user';

const CreateUser = () => {
    const [name, setName] = useState('');
    const [email, setEmail] = useState('');

    const handleSubmit = async () => {
        try {
            const user = await createUser({ name, email });
            console.log(user);
        } catch (error) {
            console.error(error);
        }
    }

    return (
        <View>
            <TextInput value={name} onChangeText={setName} placeholder="Name" />
            <TextInput value={email} onChangeText={setEmail} placeholder="Email" />
            <Button title="Create" onPress={handleSubmit} />
        </View>
    );
}

export default CreateUser;
Enter fullscreen mode Exit fullscreen mode

En este ejemplo se puede ver cómo en el componente CreateUser se llama a la función createUser del archivo usecases/create-user.js que a su vez utiliza el puerto DatabasePort.js para interactuar con el sistema externo, en este caso una base de datos.

El adaptador DatabaseAdapter.js se encarga de traducir las solicitudes y respuestas entre el formato esperado por el puerto y el formato específico de la base de datos.

La idea principal es aplicar la arquitectura hexagonal para aislar la lógica de negocio de la lógica de infraestructura y lograr una mayor escalabilidad y flexibilidad en el proyecto.

Es importante mencionar que en React Native, es recomendable utilizar un manejador de estado global, como Redux, para manejar el estado de la aplicación. Así, se podrían manejar los cambios de estado de la aplicación en un solo lugar y permitir una mayor escalabilidad y mantenimiento del código.


La Arquitectura Hexagonal en Angular

/
Estructura de carpetas:
/


src/
  app/
    create-user/
      create-user.component.ts
      create-user.component.html
      create-user.component.css
    usecases/
      create-user.ts
    ports/
      database.port.ts
    adapters/
      database.adapter.ts


Enter fullscreen mode Exit fullscreen mode

/
Archivos:
/

src/app/adapters/database.adapter.ts:


import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class DatabaseAdapter {
  constructor(private http: HttpClient) {}

  async createUser(user: any): Promise<any> {
    return this.http.post('/api/users', user).toPromise();
  }

  async getUsers(): Promise<any[]> {
    return this.http.get('/api/users').toPromise();
  }

  async updateUser(userId: string, updates: any): Promise<void> {
    return this.http.patch(`/api/users/${userId}`, updates).toPromise();
  }

  async deleteUser(userId: string): Promise<void> {
    return this.http.delete(`/api/users/${userId}`).toPromise();
  }
}


Enter fullscreen mode Exit fullscreen mode

src/app/ports/database.port.ts:


import { Injectable } from '@angular/core';
import { DatabaseAdapter } from '../adapters/database.adapter';

@Injectable({
  providedIn: 'root'
})
export class DatabasePort {
  constructor(private databaseAdapter: DatabaseAdapter) {}

  async createUser(user: any): Promise<any> {
    return this.databaseAdapter.createUser(user);
  }

  async getUsers(): Promise<any[]> {
    return this.databaseAdapter.getUsers();
  }

  async updateUser(userId: string, updates: any): Promise<void> {
    return this.databaseAdapter.updateUser(userId, updates);
  }

  async deleteUser(userId: string): Promise<void> {
    return this.databaseAdapter.deleteUser(userId);
  }
}


Enter fullscreen mode Exit fullscreen mode

src/app/usecases/create-user.ts:


import { Injectable } from '@angular/core';
import { DatabasePort } from '../ports/database.port';

@Injectable({
  providedIn: 'root'
})
export class CreateUser {
  constructor(private databasePort: DatabasePort) {}

  async execute(user) {
    try {
      return await this.databasePort.createUser(user);
    } catch (error) {
      throw error;
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

src/app/create-user/create-user.component.ts:


import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { createUser } from '../usecases/create-user';

@Component({
  selector: 'app-create-user',
  templateUrl: './create-user.component.html',
  styleUrls: ['./create-user.component.css']
})
export class CreateUserComponent implements OnInit {
  nameControl = new FormControl('');
  emailControl = new FormControl('');

  constructor() { }

  ngOnInit(): void { }

  async handleSubmit() {
    try {
      const user = await createUser({
        name: this.nameControl.value,
        email: this.emailControl.value,
      });
      console.log(user);
    } catch (error) {
      console.error(error);
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

src/app/create-user/create-user.component.html:


<form>
  <div>
    <label>Name:</label>
    <input [formControl]="nameControl" placeholder="Name">
  </div>
  <div>
    <label>Email:</label>
    <input [formControl]="emailControl" placeholder="Email">
  </div>
  <button (click)="handleSubmit()">Create</button>
</form>


Enter fullscreen mode Exit fullscreen mode

En este ejemplo, el componente CreateUserComponent se encarga de la vista de creación de usuarios. Este componente utiliza una función handleSubmit para enviar los datos del formulario a un caso de uso llamado createUser. Este caso de uso, a su vez, se comunica con un puerto llamado DatabasePort para crear el usuario en la base de datos. El puerto DatabasePort es una interfaz que define la funcionalidad necesaria para crear un usuario en la base de datos, pero no especifica cómo se lleva a cabo esa operación.

La comunicación entre el caso de uso y el puerto es independiente de la implementación, lo que significa que el caso de uso no tiene conocimiento de cómo se lleva a cabo la operación de creación de usuarios en la base de datos. En su lugar, el caso de uso describe lo que se quiere hacer (crear un usuario) y el puerto se encarga de cómo se hace (crear un usuario en una base de datos específica).

Por último, el puerto se comunica con un adaptador de base de datos llamado DatabaseAdapter, que se encarga de las tareas específicas de comunicación con la base de datos, como enviar solicitudes HTTP o realizar consultas SQL. El adaptador implementa la interfaz DatabasePort y se encarga de las tareas específicas de comunicación con la base de datos, como enviar solicitudes HTTP o realizar consultas SQL.


La Arquitectura Hexagonal en Vue

/
Estructura de carpetas:
/


src/
    adapters/
        DatabaseAdapter.js
        EmailAdapter.js
        ...
    ports/
        DatabasePort.js
        EmailPort.js
        ...
    domain/
        entities/
        usecases/
    views/
        ...
    ...


Enter fullscreen mode Exit fullscreen mode

/
Archivos:
/

src/adapters/DatabaseAdapter.js:


import axios from 'axios';

const DatabaseAdapter = {
    async findOne(url, query) {
        const result = await axios.get(url, { params: query });
        return result.data;
    },
    async save(url, data) {
        const result = await axios.post(url, data);
        return result.data;
    }
    // ...
}

export default DatabaseAdapter;


Enter fullscreen mode Exit fullscreen mode

src/ports/DatabasePort.js:

export const DatabasePort = {
    findOne: (url, query) => {},
    save: (url, data) => {},
    // ...
}

Enter fullscreen mode Exit fullscreen mode

src/domain/usecases/CreateUser.js:

import { DatabasePort } from '../ports';
import { User } from '../entities';

export const createUser = async (data) => {
    try {
        const user = new User(data);
        const savedUser = await DatabasePort.save('https://api.example.com/users', user);
        return savedUser;
    } catch (error) {
        throw new Error(error);
    }
}

Enter fullscreen mode Exit fullscreen mode

src/views/CreateUser.vue:

<template>
  <form @submit="handleSubmit">
    <input v-model="name" placeholder="Name" />
    <input v-model="email" placeholder="Email" />
    <button type="submit">Create</button>
  </form>
</template>

<script>
import { createUser } from '@/usecases'

export default {
  setup() {
    const name = ref('')
    const email = ref('')
    const handleSubmit = async () => {
      try {
        const user = await createUser({ name: name.value, email: email.value })
        console.log(user)
      } catch (error) {
        console.error(error)
      }
    }
    return {
      name,
      email,
      handleSubmit
    }
  }
}
</script>     

Enter fullscreen mode Exit fullscreen mode

En este ejemplo se puede ver como en la vista CreateUser.vue se llama a la función createUser del archivo usecases/CreateUser.js que a su vez utiliza el puerto DatabasePort.js para interactuar con el sistema externo, en este caso una base de datos.

El adaptador DatabaseAdapter.js se encarga de traducir las solicitudes y respuestas entre el formato esperado por el puerto y el formato específico de la base de datos.


Mejorando la arquitectura de tu aplicación con la arquitectura Hexagonal

Consejos y herramientas para implementar en React, React Native, Angular y Vue 🚀💕

Les dejo una lista de consejos que está enfocada en mejorar la escalabilidad y flexibilidad en aplicaciones de JavaScript.

NOTA: Algunos de los puntos que les mencionaré pueden ser aplicables en tecnologías específicas como Vue, Angular, React, y React Native.

Sin embargo, es importante que tengan en cuenta que algunos de los consejos pueden ser genéricos y aplicables en cualquier tecnología en particular, así que no me recriminen por si acaso, habrá que algunos les sirva y habrá otros que ya lo sabían, pero desde mi experiencia e investigaciones les comparto con mucho.

LOVE Bros …


Implementando la arquitectura Hexagonal en - REACT -

Consejos y herramientas para mejorar la escalabilidad:
/

  • Utiliza funciones de componentes en lugar de componentes de clase: Esto ayuda a aislar la lógica de negocio de los componentes, lo que facilita el mantenimiento y la escalabilidad de la aplicación.

  • Utiliza una librería como react-redux para manejar el estado global de la aplicación: Esto ayuda a aislar la lógica de negocio de la gestión del estado, lo que facilita el mantenimiento y la escalabilidad de la aplicación.

  • Utiliza una librería como axios o fetch para hacer peticiones HTTP desde los adaptadores: Esto ayuda a aislar la lógica de negocio de las dependencias externas, lo que facilita el mantenimiento y la escalabilidad de la aplicación.

  • Utiliza una librería como redux-thunk o redux-saga para manejar la lógica asíncrona en los casos de uso: Esto ayuda a aislar la lógica de negocio de la lógica asíncrona, lo que facilita el mantenimiento y la escalabilidad de la aplicación.

  • Utiliza una librería como normalizr para normalizar los datos almacenados en el estado: Esto ayuda a mantener una estructura de datos consistente y fácil de acceder en el estado, lo que facilita el mantenimiento y la escalabilidad de la aplicación.

  • Utiliza una librería como localforage o idb para manejar el almacenamiento local en los adaptadores: Esto ayuda a aislar la lógica de negocio de las dependencias externas, lo que facilita el mantenimiento y la escalabilidad de la aplicación.

  • Utiliza una librería como immer para manejar la inmutabilidad del estado en los casos de uso: Esto ayuda a mantener un estado inmutable y fácil de rastrear en la aplicación, lo que facilita el mantenimiento y la escalabilidad de la aplicación.

  • Utiliza una librería como Reselect para crear selectores de estado para evitar recalculos innecesarios: Esto ayuda a mejorar el rendimiento de la aplicación al evitar recalcular el estado innecesariamente.

  • Utiliza una librería como react-final-form para manejar formularios en la aplicación: Esto ayuda a aislar la lógica de negocio de la gestión de formularios, lo que facilita el mantenimiento y la escalabilidad de la aplicación.

  • Utiliza una librería como react-router para manejar el enrutamiento en la aplicación: Esto ayuda a aislar la lógica de negocio de la gestión del enrutamiento, lo que facilita el mantenimiento y la escalabilidad de la aplicación.

  • Utiliza una librería como redux-persist para persistir el estado en el cliente: Esto ayuda a mantener el estado de la aplicación entre sesiones del usuario, lo que mejora la experiencia del usuario.

  • Utiliza un contenedor de inyección de dependencias para proporcionar las dependencias del núcleo de la aplicación a los componentes de interfaz de usuario. Puedes utilizar una librería como InversifyJS

  • Utiliza la abstracción para separar la lógica de negocio del núcleo de tu aplicación de las dependencias externas, esto te ayudará a testear de manera independiente y a asegurar que los cambios en las dependencias externas no afecten el funcionamiento de tu aplicación.

Consejos y herramientas para implementar la arquitectura Hexagonal en - REACT NATIVE -

/

  • Utiliza una abstracción de nivel superior para la lógica de negocio: La arquitectura hexagonal promueve la separación de la lógica de negocio de la lógica de presentación. Esto se hace mediante la creación de una abstracción de nivel superior que contiene la lógica de negocio y expone un conjunto de servicios basados en interfaces. Esto permite que la lógica de presentación se adapte fácilmente a diferentes plataformas, como React y React Native, sin tener que modificar la lógica de negocio.

  • Utiliza un sistema de eventos para permitir la comunicación entre la lógica de negocio y la lógica de presentación: La arquitectura hexagonal utiliza un sistema de eventos para permitir la comunicación entre la lógica de negocio y la lógica de presentación. Esto permite que la lógica de presentación reciba información de la lógica de negocio de manera asíncrona. Esto facilita la reutilización de la lógica de negocio y mejora el rendimiento.

  • Utiliza un patrón de diseño de inversión de control para permitir la reutilización de la lógica de negocio: La arquitectura hexagonal utiliza el patrón de diseño de inversión de control para permitir la reutilización de la lógica de negocio. Esto permite que la lógica de negocio sea reutilizada en diferentes contextos y plataformas, lo que simplifica la creación de aplicaciones y mejora el rendimiento de la aplicación. Además, el patrón de diseño de inversión de control permite que los componentes sean reutilizados en aplicaciones diferentes, lo que permite una mayor flexibilidad y facilita la creación de aplicaciones.
  • Utiliza una estructura de bloques para organizar el contenido: La estructura de bloques permite organizar el contenido de una manera clara y estructurada. Esta estructura divide el contenido en secciones y subsecciones, lo que permite un mejor ordenamiento y hace el contenido más accesible para los lectores.

  • Utiliza componentes funcionales: Utilizar componentes funcionales te ayudará a asegurarte de que cada componente solo se encarga de presentar la interfaz de usuario y no tiene lógica de negocio.

  • Composición: al seguir la arquitectura hexagonal, se promueve el uso de la composición de componentes, lo que permite crear componentes más complejos a partir de componentes más simples, lo que facilita la reutilización y el mantenimiento del código.

  • Utiliza una librería como react-native-navigation-redux-helpers para conectar los componentes de navegación con el estado global de la aplicación manejado con Redux. Esto te permitirá manejar el flujo de la navegación en la aplicación de forma centralizada y separada de los componentes, lo que permite una mayor facilidad para el manejo de la información y su estructuración.


Consejos - Arquitectura Hexagonal en - ANGULAR -

Mejorando la escalabilidad y mantenibilidad de tu aplicación
/

Angular utilizada una arquitectura que se basa en componentes y servicios, parecida a lo que es la Arquitectura Hexagonal, la arquitectura que usa Angular promueve la división de responsabilidades entre la lógica de negocio y la de presentación.

Angular dentro de sus características promueve el seguimiento de buenas prácticas de programación, y por eso las mayorías de las veces es una buena opción, para que un programador junior comience a agarrar experiencia primero con este Framework, ya que aprenderá buenas prácticas de programación mientras construye aplicaciones web.


Ya con esto aclarado, solo podre darles algunos consejos para Angular:


  • Utiliza servicios para aislar la lógica de negocio de los componentes de Angular. Los servicios son ideales para alojar la lógica de negocio y mantenerla independiente de los componentes de la interfaz de usuario.

  • Utiliza una librería como ngrx para manejar el estado global de la aplicación y separarlo de los componentes. ngrx es una implementación de Redux para Angular que ayuda a mantener el estado global de la aplicación en un único lugar y promueve la arquitectura Hexagonal.

  • Utiliza una librería como ngrx-effects para manejar la lógica asíncrona en los servicios. ngrx-effects permite crear efectos secundarios que se activan cuando se disparan acciones específicas del estado.

  • Utiliza una librería como ngx-store para persistir el estado en el cliente. ngx-store permite guardar el estado en el navegador del usuario, lo que ayuda a mantener la aplicación sincronizada entre diferentes sesiones.

  • Utiliza una librería como ngx-auth para manejar la autenticación y autorización en la aplicación. ngx-auth proporciona un conjunto de herramientas para manejar la autenticación y autorización de manera segura, lo que ayuda a mantener la lógica de negocio aislada.

  • Utiliza una librería como ngx-logger para registrar la actividad de la aplicación en desarrollo. ngx-logger proporciona un conjunto de herramientas para registrar la actividad de la aplicación en desarrollo, lo que ayuda a depurar y solucionar problemas más fácilmente.

  • Utiliza la inyección de dependencias para pasar servicios entre componentes y aislar aún más la lógica de negocio. La inyección de dependencias ayuda a evitar el acoplamiento entre componentes y servicios y promueve la reutilización de código.

  • Utiliza el patrón Observable para manejar eventos y peticiones asíncronas. Al utilizar el patrón Observable, se promueve la modularidad y la separación de responsabilidades en la aplicación ya que los componentes solo se suscriben a los datos que necesitan y no tienen que preocuparse por cómo se obtienen esos datos. Al uso del patrón Observable en Angular promueve la arquitectura hexagonal al permitir la separación de responsabilidades entre la lógica de negocio y la lógica de presentación, lo que facilita la escalabilidad y el mantenimiento del código.


Consejos - VUE y la arquitectura Hexagonal: Cómo maximizar la eficiencia de tu aplicación:

/

  • Utilizar rutas de montaje para diferenciar mejor los componentes. Esto ayuda a mantener la separación de intereses entre los componentes y mejorar la escalabilidad. Esto ayuda a mejorar la escalabilidad al permitir la reutilización de la lógica en diferentes componentes.

  • Utilizar una lógica de presentación para encapsular la lógica de la interfaz de usuario en los componentes de Vue. Esto ayuda a separar la lógica de la interfaz de usuario de la lógica de la aplicación, lo cual es esencial para mejorar la escalabilidad y flexibilidad de la aplicación a medida que se vaya agregando más funcionalidad y complejidad.

  • Utilizar una lógica de presentación para encapsular la lógica de la interfaz de usuario. Esto ayuda a separar la lógica de la interfaz de usuario de la lógica de la aplicación.

  • Utilizar un patrón de abstracción para encapsular la lógica de la aplicación en una capa. Esto ayuda a mantener la escalabilidad y flexibilidad de la aplicación.

  • Utilizar un patrón de controlador para manejar los eventos de la interfaz de usuario. Esto ayuda a mantener la escalabilidad de la aplicación.

  • Utilizar un patrón de vista para abstraer la lógica de la interfaz de usuario. Esto ayuda a mantener la escalabilidad y modularidad de la aplicación.

  • Utilizar un patrón de delegado para delegar la responsabilidad de la lógica de la aplicación a otros objetos. Esto ayuda a reducir el código duplicado y mejorar la modularidad y escalabilidad.

  • Utilizar un patrón de intermediario para permitir la comunicación entre los componentes. Esto ayuda a mantener la escalabilidad y modularidad al permitir la comunicación entre los componentes sin tener que conocer el detalle de la implementación de los mismos.

  • Utilizar un patrón de servicio para desacoplar la lógica de la aplicación de la lógica de presentación. Esto ayuda a mejorar la escalabilidad al permitir la reutilización de la lógica.

  • Utilizar un patrón de contenedor para encapsular la lógica de la aplicación en un único contenedor. Esto ayuda a mejorar la escalabilidad al permitir el despliegue de la aplicación en entornos distribuidos.

  • Utilizar un patrón de orquestación para ejecutar una secuencia de pasos para la ejecución de una tarea. Esto ayuda a mejorar la escalabilidad al permitir la ejecución de tareas en paralelo.


-- FIN --

Top comments (8)

Collapse
 
chacaponquin profile image
Hector Angel Gomez Robaina

Gracias por este articulo. Fue muy util

Collapse
 
dennysjmarquez profile image
Dennys José Márquez Reyes

🤜🤛

Collapse
 
dennysjmarquez profile image
Dennys José Márquez Reyes

Hola @chacaponquin Gracias por tomarte el tiempo de dejar un comentario positivo.

Me alegra mucho que hayas encontrado útil este artículo en tu viaje de aprendizaje.

Mi objetivo es compartir conocimiento de forma útil. Tu comentario positivo me motivan a mejorar mis explicaciones para ayudar a más personas.

Si tienes sugerencias para mejorar mi explicación en áreas que te resultaron menos claras o que te faltaron ejemplos relevantes, házmelo saber.

Siempre busco formas de crear más valor a través de mi escritura

Collapse
 
lightyagami profile image
Light Yagami

La arquitectura hexagonal es una locura!! Gracias bro por sacar de tu tiempo para escribir este articulo, fue de gran ayuda.

Collapse
 
dennysjmarquez profile image
Dennys José Márquez Reyes • Edited

@lightyagami Gracias! Me alegra que hayas encontrado útil la explicación de la arquitectura hexagonal. ¿Qué parte te pareció más útil? Siempre estoy abierto a sugerencias para mejorar mi explicación y crear aún más valor.

Collapse
 
sanchezcarlos1986 profile image
Carlos Sánchez

Una duda Dennys: Hasta donde entiendo, los usecases deberían ir en la capa de Application y no dentro de Domain, no?

Image description

Collapse
 
dennysjmarquez profile image
Dennys José Márquez Reyes

Así es los casos de uso no deberían ir dentro de la capa de dominio, ya que esto violaría la separación de preocupaciones y la independencia entre capas.

Los casos de uso deberían ir dentro de la capa de aplicación, que es la responsable de coordinar las diferentes partes del sistema para implementar las operaciones del negocio

Collapse
 
manuxdjent profile image
manuxdjent

Tengo una duda Dennys,
Veo como un puerto usa un adaptador, no debería de ser al revés? creo que el puerto define como se debe de implementar el adaptador.
Un saludo y gracias por el articulo!