DEV Community

Christian Gonzales Komiya
Christian Gonzales Komiya

Posted on

(Spanish) Arquitectura Hexagonal (Ports & Adapters) - Beneficios y Ejemplo en Java

Mientras leía el libro Hexagonal Architecture Explained de Alistair Cockburn y Juan Manuel Garrido, me encontré con una sección que destaca beneficios clave de este patrón.

En este artículo represento en código Java los tres pasos esenciales para implementar la arquitectura y lograr dichos beneficios.


Beneficios destacados

  • El sistema queda abierto a tener pruebas de regresión para todas sus funciones, de extremo a extremo.
  • Es posible variar las tecnologías externas conforme evolucionan (como siempre lo hacen) sin un esfuerzo enorme.
  • Estos dos beneficios adicionales superan fácilmente el pequeño costo extra de implementar la solución.

Paso 1 – API del sistema (interfaz proporcionada)

Creamos la aplicación para que sus servicios estén disponibles como llamadas a funciones, sin depender directamente de UI o base de datos.

// Paso 1: API del sistema (interfaz proporcionada)
public class TaxCalculatorApp {

    // Servicio principal expuesto como API
    public double calculateTax(double amount, double rate) {
        return amount * rate;
    }
}
Enter fullscreen mode Exit fullscreen mode

Aquí la aplicación no sabe nada de dónde viene el rate ni de si hay base de datos: es solo lógica pura.


Paso 2 – Variables para las conexiones externas (interfaces requeridas)

Agregamos variables para cada conexión externa (por ejemplo, un servicio que da la tasa de impuestos).

// Interfaz requerida: servicio externo para obtener tasa de impuestos
public interface TaxRateProvider {
    double getTaxRate();
}

// Paso 2: la aplicación ahora tiene una variable para su conexión externa
public class TaxCalculatorApp {

    // Variable para el proveedor de tasas (actor secundario)
    private TaxRateProvider taxRateProvider;

    // Inyectamos la dependencia (aún puede ser null si no se configura)
    public void setTaxRateProvider(TaxRateProvider provider) {
        this.taxRateProvider = provider;
    }

    // Servicio principal usa la interfaz requerida
    public double calculateTax(double amount) {
        double rate = taxRateProvider.getTaxRate();
        return amount * rate;
    }
}
Enter fullscreen mode Exit fullscreen mode

Ahora la aplicación no llama directamente a un servicio concreto: usa una interfaz requerida (TaxRateProvider).


Paso 3 – Configuración con actores reales o adaptadores (inyección de dependencias)

En la construcción/configuración, conectamos la app con un adaptador concreto, o con un doble de pruebas.

// Adaptador concreto que obtiene tasa desde una BD (ejemplo simple)
public class DatabaseTaxRateAdapter implements TaxRateProvider {
    @Override
    public double getTaxRate() {
        // Aquí iría la lógica real de conexión a base de datos
        return 0.21; // Ejemplo: IVA 21%
    }
}

// Adaptador doble de pruebas
public class FixedTaxRateStub implements TaxRateProvider {
    private final double fixedRate;
    public FixedTaxRateStub(double fixedRate) {
        this.fixedRate = fixedRate;
    }
    @Override
    public double getTaxRate() {
        return fixedRate;
    }
}

// Paso 3: Configuración y uso
public class Main {
    public static void main(String[] args) {
        TaxCalculatorApp app = new TaxCalculatorApp();

        // Configuración para producción
        TaxRateProvider dbAdapter = new DatabaseTaxRateAdapter();
        app.setTaxRateProvider(dbAdapter);
        System.out.println("Impuesto: " + app.calculateTax(100));

        // Configuración para pruebas
        TaxRateProvider testStub = new FixedTaxRateStub(0.10);
        app.setTaxRateProvider(testStub);
        System.out.println("Impuesto (prueba): " + app.calculateTax(100));
    }
}
Enter fullscreen mode Exit fullscreen mode

✅ Qué logramos siguiendo los pasos

  • La app expone su lógica a través de una API clara (calculateTax), sin UI ni BD acoplada.
  • Creamos variables para las dependencias externas (TaxRateProvider).
  • Conectamos esas dependencias en tiempo de configuración, ya sea con adaptadores reales o dobles de prueba.

Top comments (0)