Muchas capas arquitectónicas no agregan valor
Añadimos capas por reflejo, convencidos de que separar es siempre desacoplar. Pero una capa que solo reenvía datos de una a otra no aísla nada: agrega ceremonia, costo de navegación y la ilusión de un diseño limpio. Una capa solo se gana su lugar cuando absorbe un cambio que de otro modo se propagaría.
Hay un reflejo muy arraigado en la ingeniería de software: cuando algo se siente desordenado, agregamos una capa. Una capa de servicios sobre los repositorios. Una capa de DTOs entre el dominio y la API. Una capa de mappers entre los DTOs y las entidades. Una fachada sobre la fachada. Cada capa se justifica con la misma palabra mágica: desacoplamiento. Y casi nadie se detiene a preguntar si esa capa realmente desacopla algo, o si solo añade un lugar más por donde los datos tienen que pasar.
Porque eso es lo incómodo: muchas de las capas que escribimos no agregan valor. Solo agregan tránsito.
La capa que solo reenvía
La señal más clara de una capa inútil es que, cuando la abres, lo único que hace es llamar a la capa de abajo y devolver el resultado casi sin tocarlo.
El controlador llama al servicio. El servicio, sin lógica propia, llama al repositorio. El repositorio llama al ORM. En cada salto se copia el objeto a una estructura nueva con los mismos campos pero distinto nombre, y se vuelve a copiar de regreso en el camino de salida. Cinco archivos, cuatro conversiones, cero decisiones. Si borraras la capa intermedia y conectaras directamente las dos que la rodean, el comportamiento del sistema sería idéntico.
Eso es lo que yo llamo una capa de paso: una capa que no toma ninguna decisión, no impone ninguna regla y no oculta ninguna variación. Su único efecto es que ahora hay que escribir más código para hacer lo mismo, y leer más código para entender qué pasa. La hicimos porque “así se hace”, porque el diagrama de la arquitectura tenía ese rectángulo, no porque resolviera un problema concreto.
El error de fondo: confundir separar con desacoplar
La raíz del problema es una confusión sutil pero costosa: creer que separar el código en más piezas es lo mismo que desacoplarlo.
No lo es. Separar es repartir el código en más archivos, más clases, más capas. Desacoplar es lograr que un cambio en una parte no obligue a cambiar otra. Son cosas distintas, y de hecho a veces opuestas: puedes separar mucho y seguir totalmente acoplado.
El ejemplo clásico es la capa de servicios anémica que solo delega al repositorio. Hay dos clases en lugar de una, así que el código está “separado”. Pero si agregas un campo a la entidad, tienes que tocar el repositorio, el servicio, el DTO, el mapper y el controlador, todos a la vez. El cambio se propaga por las cinco capas. No desacoplaste nada: solo repartiste el mismo acoplamiento en más lugares, y le pusiste el nombre de “arquitectura en capas”.
Una capa solo desacopla si absorbe la variación: si un cambio que ocurre de un lado de la capa muere ahí y no cruza al otro lado. Si el cambio la atraviesa sin esfuerzo, la capa no es una frontera, es un peaje.
Lo que sí justifica una capa
Esto no es un alegato contra las capas. Las capas son una de las herramientas más poderosas que tenemos para domar la complejidad. El punto es que cada capa tiene que ganarse su lugar, y hay criterios bastante claros para saber cuándo lo hace.
Una capa agrega valor cuando aísla una variación real: cuando del otro lado hay algo que cambia de forma independiente y la capa evita que ese cambio se propague. Una capa de acceso a datos que te permite cambiar de Postgres a otra cosa sin tocar el dominio justifica su existencia — si esa variación es plausible y la capa de verdad la contiene.
Una capa agrega valor cuando traduce entre dos modelos genuinamente distintos: cuando la forma en que el mundo externo habla (el JSON de una API pública, el formato de un sistema legado) no debe contaminar la forma en que tu dominio piensa. Ahí el mapper no es ceremonia: es la aduana que protege la integridad del modelo interno.
Una capa agrega valor cuando concentra una decisión transversal: autorización, transacciones, manejo de errores, observabilidad. Cosas que, si no tuvieran un lugar propio, quedarían dispersas y repetidas por todo el código.
El patrón común en los tres casos es el mismo: la capa contiene algo que, sin ella, se desbordaría hacia el resto del sistema. Si no hay nada que contener, no hay capa que justificar.
El costo que pagamos por las capas vacías
Las capas de paso no son gratis aunque parezcan inofensivas. Su costo es real, solo que se paga en cuotas pequeñas que cuesta atribuir.
Costo de navegación. Para entender un flujo simple, el lector tiene que saltar por cinco archivos siguiendo llamadas que no hacen nada. Cada salto consume atención y memoria de trabajo. Entender qué pasa cuando llega una petición se vuelve una expedición en lugar de una lectura.
Costo de cambio. Como el acoplamiento sigue ahí, cada modificación real obliga a tocar todas las capas. Agregar un campo no es un cambio en un lugar: es el mismo cambio replicado cinco veces, con cinco oportunidades de cometer un error o de olvidar una.
Costo de credibilidad. Cuando la mitad de las capas no hacen nada, se pierde la señal de cuáles sí importan. El lector deja de confiar en que una capa significa algo, y empieza a asumir que toda capa es ruido. Las fronteras importantes se vuelven invisibles entre tanta frontera decorativa.
Costo de imitación. El código nuevo copia la estructura del código viejo. Si el patrón establecido es “todo lleva su capa de servicio vacía”, la siguiente persona la creará también, por consistencia, aunque tampoco la necesite. Las capas inútiles se reproducen solas.
Por qué las agregamos de todos modos
Si el costo es real, ¿por qué seguimos añadiéndolas? Por razones humanas y entendibles.
Por cargo culto: vimos esta estructura en un proyecto exitoso, en un tutorial, en un libro, y la replicamos sin el contexto que la hacía necesaria. Copiamos la forma sin copiar el problema que la forma resolvía.
Por miedo al futuro: “algún día quizás necesitemos cambiar la base de datos / exponer otra API / soportar otro proveedor”. Así que ponemos la capa por si acaso. Pero una abstracción especulativa, construida para una variación que tal vez nunca llegue, paga su costo hoy a cambio de un beneficio hipotético. Y cuando el cambio finalmente llega, casi nunca tiene la forma que imaginamos, así que la capa preventiva tampoco sirve.
Por simetría estética: si esta entidad tiene servicio, repositorio, DTO y mapper, las demás “deberían” tenerlos también, aunque sean triviales. La consistencia visual del diagrama se vuelve más importante que la utilidad real de cada pieza.
Todas estas razones tienen algo en común: la capa se agrega para satisfacer una idea abstracta de cómo debe verse una buena arquitectura, no para resolver un problema que el sistema tenga de verdad.
La prueba práctica
Hay una pregunta sencilla que filtra la mayoría de las capas inútiles antes de que existan: ¿qué cambio futuro hace que esta capa valga la pena, y qué tan probable es ese cambio?
Si puedes nombrar una variación concreta y plausible que la capa absorbería — y articular cómo el cambio moriría dentro de ella sin propagarse — la capa probablemente se justifica. Si la única respuesta es “para desacoplar” en abstracto, o “por si acaso”, o “porque así se hace”, es muy probable que estés a punto de construir un peaje.
Una segunda prueba, para el código que ya existe: intenta borrar la capa mentalmente y conectar directo lo que la rodea. Si el sistema queda igual de comprensible y de flexible, la capa no estaba aportando nada. Bórrala de verdad. Quitar una capa de paso casi siempre hace el código más fácil de leer y más honesto sobre dónde están los acoplamientos reales.
La regla por defecto debería invertirse. En vez de agregar capas hasta que el diseño se sienta “completo”, empieza con la mínima estructura que resuelve el problema y agrega una capa solo cuando un dolor concreto lo exija. Es mucho más fácil introducir una frontera cuando la necesitas que quitar diez que nunca necesitaste.
Cierre
Las capas no son virtud por sí mismas. Una arquitectura no es mejor por tener más rectángulos en el diagrama, igual que un texto no es mejor por tener más palabras. El valor de una capa no está en que separe el código, sino en que contenga un cambio.
Antes de añadir la siguiente, vale la pena hacerse la pregunta incómoda: ¿esta capa absorbe alguna variación real, o solo está reenviando datos con un nombre más elegante? Si es lo segundo, lo más limpio —y lo más valiente— casi nunca es agregar otra abstracción. Es quitar la que sobra.
La buena arquitectura no se mide por cuántas capas tiene, sino por cuántas de ellas se ganaron de verdad su lugar.