¿Estás aplicando un anti-patrón de arquitectura en el Frontend?
Source: Dev.to
¿Qué es la arquitectura de software?
Arquitectura de software es la estructura de alto nivel de un sistema de software, la disciplina de crear tales estructuras, y la documentación de estas estructuras. — Software Architecture in Practice, Bass et al.
En palabras simples, la arquitectura es la estructura que define cómo se organizarán los diferentes componentes de un sistema y cómo interactuarán entre sí. Además, establece las reglas y principios que guiarán el desarrollo.
Una buena arquitectura:
- Facilita un flujo de trabajo consistente para todos los desarrolladores.
- Permite que nuevos equipos o miembros se sumen al proyecto sin mayores problemas.
- Hace que la base de código sea más mantenible, escalable y testeable.
- Incrementa la calidad del software y la productividad del equipo.
Pilares fundamentales de una buena arquitectura
| Pilar | Descripción | Ejemplo práctico |
|---|---|---|
| Separación de responsabilidades | Cada módulo tiene una única responsabilidad. | Componentes UI separados de la lógica de negocio. |
| Bajo acoplamiento | Los módulos dependen lo menos posible entre sí. | Uso de interfaces y abstracciones. |
| Alta cohesión | Elementos relacionados están agrupados. | Features o dominios bien definidos. |
| Escalabilidad | El sistema puede crecer sin reestructuración mayor. | Arquitectura modular. |
| Testeabilidad | Facilidad para escribir y ejecutar tests. | Dependencias inyectables. |
Anti‑patrones de arquitectura
Un anti‑patrón es una respuesta común a un problema recurrente que es usualmente inefectiva y tiene el riesgo de ser altamente contraproducente. — AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis
Hipótesis vs. contra‑hipótesis
| Hipótesis | Contra‑hipótesis |
|---|---|
| Una buena arquitectura mantiene un buen flujo de trabajo para todo el equipo de desarrollo. | Un anti‑patrón de arquitectura puede generar un mal flujo de trabajo para todo el equipo de desarrollo. |
| Una buena arquitectura facilita la incorporación de nuevos miembros al equipo de desarrollo. | Un anti‑patrón de arquitectura puede dificultar la incorporación de nuevos miembros al equipo de desarrollo. |
| Una buena arquitectura hace que la base de código sea más mantenible, escalable y testeable. | Un anti‑patrón de arquitectura puede hacer que la base de código sea menos mantenible, escalable y testeable. |
| Una buena arquitectura incrementa la calidad del software y la productividad de los equipos de desarrollo. | Un anti‑patrón de arquitectura puede disminuir la calidad del software y la productividad de los equipos de desarrollo. |
Anti‑patrones más comunes en el frontend
1. God Component (Componente dios)
Síntomas
- No existe una separación clara de capas.
- Componentes con múltiples responsabilidades (más de 500 líneas).
- Dependencias circulares entre módulos.
- Dificultad para encontrar dónde está la lógica de una funcionalidad.
- Los cambios en un lugar rompen funcionalidades en otros lugares.
Ejemplo de un God Component (mal)
// UserDashboard.jsx - 800+ líneas
function UserDashboard() {
const [user, setUser] = useState(null);
const [orders, setOrders] = useState([]);
const [notifications, setNotifications] = useState([]);
const [settings, setSettings] = useState({});
const [isLoading, setIsLoading] = useState(true);
// ... 50 más estados
useEffect(() => {
// Fetch user, orders, notifications, settings...
// Lógica de transformación de datos
// Manejo de errores
// WebSocket connections
}, [/* muchas dependencias */]);
const handleUpdateProfile = async () => { /* ... */ };
const handleDeleteOrder = async () => { /* ... */ };
const handleMarkNotificationRead = async () => { /* ... */ };
// ... 30 más handlers
return (
// 500+ líneas de JSX
);
}
Solución – Composición y separación
- Dividir la UI en componentes pequeños y reutilizables (p. ej.,
UserProfile,OrderList,NotificationPanel). - Externalizar la lógica de negocio y de acceso a datos a hooks o a servicios independientes (
useUser,useOrders,apiClient). - Gestionar el estado global con una solución adecuada (Redux, Zustand, Context API) y mantener el estado local solo para UI.
- Aplicar la regla de una única responsabilidad a cada archivo/módulo.
// UserDashboard.jsx – composición
import UserProfile from './UserProfile';
import OrderList from './OrderList';
import NotificationPanel from './NotificationPanel';
import SettingsPanel from './SettingsPanel';
export default function UserDashboard() {
return (
<>
<UserProfile />
<OrderList />
<NotificationPanel />
<SettingsPanel />
</>
);
}
// useUser.js – hook de lógica de negocio
import { useState, useEffect } from 'react';
import apiClient from '../api/apiClient';
export function useUser() {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
apiClient.getUser()
.then(data => setUser(data))
.finally(() => setIsLoading(false));
}, []);
return { user, isLoading };
}
Conclusión
Los anti‑patrones de arquitectura aparecen de forma inadvertida, pero reconocer sus síntomas y aplicar soluciones basadas en separación de responsabilidades, bajo acoplamiento y alta cohesión permite mantener un código limpio, escalable y fácil de testear.
Al adoptar buenas prácticas desde el inicio y revisar periódicamente la estructura del proyecto, evitaremos que el frontend se convierta en un “código espagueti” y garantizaremos una experiencia de desarrollo más productiva y sostenible.
Anti‑patrones de arquitectura en el frontend
1. Dependencias en cascada
“Se convierte en una pesadilla.”
Síntomas
- Cambiar un componente requiere modificar muchos otros.
- Los tests requieren mockear muchas dependencias.
- Los componentes importan directamente de rutas profundas de otros módulos.
- Props drilling excesivo (pasar props a través de 5 + niveles).
Solución – Inversión de dependencias
Crear una carpeta que actúe como cajón de sastre donde irán los módulos que “no sabemos dónde poner”.
Estructura problemática (Mal)
src/
├── utils/
│ ├── helpers.js # 2000 líneas
│ ├── functions.js # 1500 líneas
│ ├── misc.js # ???
│ ├── common.js # Más de lo mismo
│ └── index.js # Re‑exporta todo
2. Estado global excesivo
“Usar estado global para todo, incluso para estado que debería ser local.”
Síntomas
- El store global tiene cientos de propiedades.
- Estado de formularios en Redux/Zustand.
- Valores temporales o de UI en estado global.
- Dificultad para rastrear qué modifica qué.
- Re‑renders innecesarios en toda la aplicación.
Solución – Separación de responsabilidades de estado
Evitar crear abstracciones complejas antes de que sean necesarias (“por si acaso”).
Causas comunes
| Motivo | Descripción |
|---|---|
| Lectura reciente de un patrón | Querer aplicarlo sin necesidad real. |
| Miedo al refactor futuro | Prefiero “pre‑optimizar”. |
| Presión por código “profesional” | Creer que la complejidad es sinónimo de calidad. |
| No distinguir complejidad esencial vs. accidental | Añadir capas sin valor. |
3. Sobre‑ingeniería de abstracciones
“Crear abstracciones complejas antes de que sean necesarias.”
Preguntas de autodiagnóstico
| Pregunta | Si la respuesta es NO… |
|---|---|
| ¿Realmente necesito esta abstracción ahora? | Espera a tener el caso de uso real. |
| ¿Estoy resolviendo un problema real o uno hipotético? | No resuelvas problemas que no existen. |
| ¿Esta abstracción añade valor o solo complejidad? | Mantén la solución simple. |
| ¿Puedo implementar esto de manera más simple primero? | Hazlo simple y refactoriza después. |
Principio de la Regla del Tres
Cuando una solución se repite tres veces, entonces vale la pena abstraerla.
Mal – Sobre‑ingeniería para un simple botón
// 8 archivos, 200+ líneas para... un botón
interface IButtonStrategy {
execute(): void;
}
interface IButtonProps {
strategy: IButtonStrategy;
builder: IButtonBuilder;
}
class SubmitButtonStrategy implements IButtonStrategy {
constructor(private validator: IFormValidator) {}
execute(): void { /* ... */ }
}
class CancelButtonStrategy implements IButtonStrategy { /* ... */ }
class ButtonStrategyFactory { /* ... */ }
class ButtonBuilder implements IButtonBuilder { /* ... */ }
class AbstractButton extends BaseComponent { /* ... */ }
// Render final:
// Submit
Bien – Solución simple que resuelve el problema real
// Un componente, ~30 líneas
interface ButtonProps {
variant: 'submit' | 'cancel' | 'default';
onClick: () => void;
children: React.ReactNode;
disabled?: boolean;
}
function Button({ variant, onClick, children, disabled }: ButtonProps) {
const styles = {
submit: 'bg-blue-500 text-white',
cancel: 'bg-gray-200 text-gray-700',
default: 'bg-white border border-gray-300',
};
return (
<button
className={styles[variant]}
onClick={onClick}
disabled={disabled}
>
{children}
</button>
);
}
“Make it work, make it right, make it fast.” — Kent Beck
“Duplication is far cheaper than the wrong abstraction.” — Sandi Metz
4. Duplicación innecesaria vs. abstracción prematura
“Copiar y pegar código en lugar de crear abstracciones reutilizables cuando SÍ son necesarias.”
Síntomas
- Múltiples componentes con código casi idéntico.
- Cambios que requieren modificar muchos archivos.
- Bugs que se arreglan en un lugar pero persisten en otros.
Checklist de evaluación
| Pregunta | ✅ Sí | ❌ No |
|---|---|---|
| ¿Un nuevo desarrollador puede entender la estructura? | ||
| Si marcaste varios NO, es señal de que hay anti‑patrones presentes. |
5. Estrategia de refactorización gradual
-
Documenta tu arquitectura
- Usa ADRs (Architecture Decision Records) para registrar el “por qué” de cada decisión.
-
Establece convenciones
- Define una estructura de carpetas clara y consistente.
-
Revisa el código en equipo
- Los code reviews ayudan a detectar anti‑patrones temprano.
-
Mide y monitorea
- Herramientas: SonarQube, ESLint (reglas de complejidad), métricas de cobertura.
-
Aprende de otros
- Estudia arquitecturas probadas: Feature‑Sliced Design, Atomic Design, Clean Architecture adaptada al frontend.
Conclusión
Los anti‑patrones de arquitectura son como deuda técnica silenciosa: no los notamos hasta que el proyecto se vuelve difícil de mantener.
Claves para evitarlos
- Detectar temprano – Revisa regularmente la salud del código.
- Aprender continuamente – Estudia patrones de arquitectura.
- Comunicar con el equipo – Las decisiones de arquitectura son decisiones de equipo.
- Balancear – Ni sobre‑ingeniería ni código espagueti.
¿Has identificado alguno de estos anti‑patrones en tus proyectos?
¡Comparte tu experiencia en los comentarios!
Referencias
- Clean Architecture – Robert C. Martin
- Patterns of Enterprise Application Architecture – Martin Fowler
- AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis – Brown et al.
¿Te gustó este artículo? Sígueme para más contenido sobre arquitectura y desarrollo frontend.