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;
}
}
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;
}
}
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));
}
}
✅ 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)