Definición
Imagina que tu empresa moderna necesita integrarse con un sistema bancario de los años 80 que usa códigos crípticos, fechas en formatos extraños, y nombres de campos que nadie entiende ya. Podrías dejar que esos conceptos “legacy” invadan todo tu código nuevo, creando un desastre de mantenimiento. O podrías crear un traductor - una capa que convierte ese lenguaje antiguo al tuyo y viceversa. Esa capa es la Capa Anti-Corrupción (ACL).
La ACL es un patrón de Domain-Driven Design que protege tu código limpio de la “corrupción” de sistemas externos. En lugar de que tu modelo de dominio tenga que entender que “CUST_STAT_CD = ‘A’” significa “cliente activo”, la ACL hace la traducción. Tu código interno solo conoce un objeto Customer con un campo status que dice “active”. La fealdad del sistema externo queda contenida en la capa de traducción.
¿Por qué molestarse? Porque los sistemas externos cambian, se reemplazan, o se multiplican. Si su lógica está esparcida por todo tu código, cada cambio es una pesadilla. Con una ACL, solo cambias la capa de traducción. Además, tu equipo puede trabajar con conceptos limpios sin necesitar entender los misterios del mainframe de 1987. El conocimiento legacy queda encapsulado en un solo lugar.
Ejemplo
Integración con Mainframe Bancario: Tu fintech moderna necesita consultar saldos de un sistema COBOL. El mainframe devuelve ACCT_BAL_AMT: "000000012345" (centavos sin decimales, 12 caracteres fijos). Tu ACL traduce esto a { balance: 123.45, currency: "EUR" }. Tu aplicación nunca ve el formato arcano - solo números decimales limpios.
Migración de Procesador de Pagos: Cambias de Stripe a un nuevo procesador. En lugar de modificar cien archivos, tu ACL traduce entre tu modelo interno de “Payment” y los diferentes formatos de cada procesador. Hoy traduce a formato Stripe, mañana a formato del nuevo procesador. El resto de tu código no cambia.
Multi-Carrier de Envíos: Tu e-commerce trabaja con FedEx, UPS y DHL. Cada uno tiene APIs completamente diferentes, formatos de direcciones distintos, códigos de estado incompatibles. Tu ACL unifica todo: internamente manejas un objeto “Shipment” estándar. La ACL traduce a/desde cada carrier específico.
Sistema ERP Legacy: El ERP de tu empresa tiene 20 años y una base de datos con nombres de columnas de 8 caracteres y tablas sin documentar. Tu nueva aplicación web usa una ACL que traduce ITMQTY01 a inventoryQuantityWarehouse1. Los desarrolladores nuevos pueden trabajar sin arqueología de código.
Analogía
El Traductor en la Embajada: Cuando un diplomático español necesita comunicarse con uno japonés, no aprende japonés ni espera que el otro aprenda español. Un traductor convierte cada mensaje al idioma del receptor. La ACL es ese traductor - convierte el “idioma” del sistema externo al tuyo y viceversa.
El Adaptador de Enchufe Universal: Cuando viajas a otro país, no cambias todos tus dispositivos - usas un adaptador que traduce entre el enchufe local y tus aparatos. La ACL es un adaptador de software: convierte la “electricidad” del sistema externo al formato que tu sistema espera.
El Mando Universal del TV: No quieres aprender los botones de cada marca de TV, soundbar y streaming. Un mando universal te da una interfaz consistente, y traduce tus comandos al protocolo específico de cada dispositivo. La ACL hace lo mismo para sistemas de software.
El Traductor de Cocina: Imagina una receta antigua que dice “un pellizco de esto, dos dedos de aquello.” Un chef moderno crea una “traducción” con medidas precisas: “2 gramos de sal, 50ml de aceite.” Tu código no tiene que interpretar “pellizcos” - trabaja con unidades estándar. La ACL traduce las recetas legacy a formato moderno.
Code Example
// Respuesta de API legacy externa
const legacyResponse = {
CUST_ID: "12345",
CUST_NM: "Juan Pérez",
ADDR_LN_1: "Calle Mayor 123",
STAT_CD: "A"
};
// Capa Anti-Corrupción
class CustomerACL {
toDomain(legacyCustomer) {
return {
id: legacyCustomer.CUST_ID,
name: legacyCustomer.CUST_NM,
address: {
street: legacyCustomer.ADDR_LN_1
},
status: this.mapStatus(legacyCustomer.STAT_CD)
};
}
mapStatus(statusCode) {
const mapping = {
'A': 'active',
'I': 'inactive',
'P': 'pending'
};
return mapping[statusCode] || 'unknown';
}
toLegacy(domainCustomer) {
// Traducir modelo de dominio de vuelta a formato legacy
return {
CUST_ID: domainCustomer.id,
CUST_NM: domainCustomer.name,
STAT_CD: this.reverseMapStatus(domainCustomer.status)
// ... mapeo inverso
};
}
reverseMapStatus(status) {
const mapping = {
'active': 'A',
'inactive': 'I',
'pending': 'P'
};
return mapping[status] || 'U';
}
}
// Uso en tu aplicación
const acl = new CustomerACL();
const customer = acl.toDomain(legacyResponse);
// customer ahora es un objeto limpio que tu dominio entiende