15 de octubre de 2025

Arquitectura Hexagonal en el Frontend: Escalabilidad Real

Descubre cómo desacoplar la lógica de negocio de la interfaz de usuario para crear aplicaciones web mantenibles, testables y duraderas.

Arquitectura Hexagonal en el Frontend: Escalabilidad Real

En el vertiginoso mundo del desarrollo frontend, los frameworks nacen y mueren con una velocidad pasmosa. React, Vue, Svelte, Solid… todos compiten por nuestra atención. Sin embargo, hay algo que permanece constante: la necesidad de escribir código mantenible.

Aquí es donde entra la Arquitectura Hexagonal (o Puertos y Adaptadores). Originalmente concebida para el backend, sus principios son vitales para aplicaciones frontend modernas que aspiran a sobrevivir más allá del hype del momento.

El Problema del Acoplamiento

La mayoría de las aplicaciones frontend sufren de un acoplamiento excesivo entre la vista (Componentes React, por ejemplo) y la lógica de negocio.

// ❌ Mal: Lógica de negocio mezclada con la UI
const UserProfile = () => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch('/api/user').then(res => res.json()).then(data => {
      // Lógica de transformación de datos aquí mismo
      const formattedUser = { ...data, fullName: `${data.name} ${data.surname}` };
      setUser(formattedUser);
    });
  }, []);

  if (!user) return <div>Cargando...</div>;
  return <h1>{user.fullName}</h1>;
};

Este enfoque tiene problemas graves:

  1. Difícil de testear: Necesitas montar el componente y mockear fetch para probar una simple transformación de datos.
  2. Difícil de cambiar: Si cambias de API REST a GraphQL, tienes que tocar tus componentes UI.
  3. Difícil de reutilizar: La lógica está atrapada dentro del componente.

La Solución Hexagonal

La Arquitectura Hexagonal propone dividir la aplicación en capas concéntricas:

  1. Dominio (Núcleo): Las entidades y reglas de negocio puras. No sabe nada de React, ni de HTTP, ni del navegador.
  2. Aplicación (Casos de Uso): Orquesta el flujo de datos. “ObtenerUsuario”, “ActualizarPerfil”.
  3. Infraestructura (Adaptadores): La implementación concreta. Llamadas a API, LocalStorage, Componentes de UI.

Implementación Práctica

Definimos nuestro Dominio:

// domain/user.ts
export interface User {
  id: string;
  name: string;
  email: string;
}

export interface UserRepository {
  getUser(): Promise<User>;
}

Creamos un Caso de Uso:

// application/get-user.ts
import { UserRepository } from '../domain/user';

export class GetUserUseCase {
  constructor(private readonly repository: UserRepository) {}

  async execute(): Promise<User> {
    return await this.repository.getUser();
  }
}

Implementamos la Infraestructura (El repositorio):

// infrastructure/api-user-repository.ts
import { User, UserRepository } from '../domain/user';

export class ApiUserRepository implements UserRepository {
  async getUser(): Promise<User> {
    const response = await fetch('/api/user');
    const data = await response.json();
    return {
      id: data.uuid,
      name: data.first_name,
      email: data.email
    }; // Adaptamos la respuesta de la API a nuestro Dominio
  }
}

Y finalmente, la Vista (React):

// ui/UserProfile.tsx
const UserProfile = ({ getUserUseCase }: { getUserUseCase: GetUserUseCase }) => {
  const { data: user } = useQuery(['user'], () => getUserUseCase.execute());

  if (!user) return <div>Cargando...</div>;
  return <h1>{user.name}</h1>;
};

Conclusión

Al aplicar estos principios, logramos:

  • Independencia del Framework: Podrías migrar de React a Vue llevándote toda tu lógica de dominio y aplicación intacta.
  • Testabilidad: Puedes testear tus casos de uso con tests unitarios rápidos, sin renderizar componentes.
  • Mantenibilidad: Los cambios en la API externa solo afectan al adaptador de infraestructura, no a toda la app.

En Plano Binario, aplicamos esta rigurosidad en cada proyecto, asegurando que tu inversión tecnológica sea duradera y robusta.