Controllers gordos en Laravel
Un controlador debería coordinar la entrada HTTP, no concentrar validación, reglas de negocio, consultas, autorización, transformaciones, envíos de email y decisiones completas de flujo.

El controlador no debería ser el lugar donde vive todo
En Laravel es muy fácil empezar resolviendo lógica directamente dentro del controller. La petición llega ahí, el framework facilita validar, consultar modelos, devolver JSON y disparar procesos. Para una primera versión, esa velocidad puede ser útil.
El problema aparece cuando ese patrón se convierte en la norma. El método store empieza validando, luego decide permisos, después consulta varios modelos, calcula totales, crea registros, envía notificaciones y construye una respuesta manual. El controller deja de ser una puerta de entrada y se convierte en el caso de uso completo.
Un controlador sano coordina. Recibe la request, delega validación, autorización y ejecución, y devuelve una respuesta. Cuando absorbe todo el comportamiento, la aplicación puede seguir funcionando, pero cada endpoint se vuelve más difícil de leer, probar y reutilizar.
Señales de un controller demasiado grande
La señal más evidente son métodos store, update o destroy de muchas líneas. Pero el tamaño no es el único indicador. También conviene observar si hay validación manual dentro del método, queries complejas, condiciones de negocio mezcladas con HTTP, autorización repetida o transformaciones de respuesta escritas a mano.
Otro síntoma habitual es encontrar envíos de email, jobs, notificaciones o integraciones ejecutándose directamente desde el controller. A veces también aparecen try/catch genéricos que ocultan errores reales, código duplicado entre endpoints o respuestas inconsistentes según quién implementó cada método.
Si para testear un comportamiento de negocio necesitas preparar una petición completa, autenticar usuario, montar demasiadas dependencias y atravesar todo HTTP, probablemente el caso de uso está demasiado pegado al controller.
Por qué pasa tanto
El controller es el primer punto visible cuando llega una petición. En un proyecto joven, resolver ahí parece natural: está a mano, se entiende rápido y no exige decidir estructura. La presión de entregar funcionalidades refuerza esa decisión porque crear una Action o un Service parece trabajo adicional.
El problema es acumulativo. Una regla aislada no duele. Diez reglas repartidas entre varios controllers sí. Cuando no existe una capa de aplicación clara, cada endpoint termina resolviendo su propia versión del proceso. Eso genera duplicidad, inconsistencias y una arquitectura que depende demasiado de HTTP.
Laravel no obliga a escribir controllers gordos. Simplemente hace muy barato empezar así. Por eso, cuando la aplicación ya sostiene negocio, conviene revisar si esa comodidad inicial está frenando la mantenibilidad.
Qué riesgos genera
Un controller grande hace que los endpoints sean más difíciles de mantener. Cambiar una regla puede afectar a validación, persistencia, respuesta, notificaciones y permisos en el mismo método. El coste cognitivo sube porque el lector tiene que entender muchas capas a la vez.
También aparecen tests más frágiles. Si el comportamiento vive dentro del controller, probarlo sin pasar por HTTP es complicado. Eso empuja a escribir tests de feature muy amplios para reglas que podrían probarse de forma más directa si estuvieran en una Action o Service.
El acoplamiento con HTTP dificulta reutilizar lógica en comandos, jobs, procesos internos o APIs alternativas. Un cambio pequeño puede tener impacto inesperado porque la lógica no está aislada. A medio plazo, refactorizar da más miedo que seguir acumulando código.
Cómo adelgazar un controller
La primera extracción suele ser la validación. Form Requests permiten sacar reglas HTTP del método y dejar el controller más limpio. La autorización debería moverse a Policies o Gates cuando la decisión se repite o representa una regla clara de acceso.
Los casos de uso encajan bien en Actions o Services. No importa tanto el nombre como la responsabilidad: una clase que exprese “crear pedido”, “aprobar solicitud” o “sincronizar cliente” permite probar el flujo sin depender de HTTP. Las respuestas pueden delegarse en API Resources para evitar transformaciones manuales dispersas.
Procesos asíncronos, emails pesados o integraciones deberían ir a Jobs cuando tenga sentido. Events y Listeners pueden ayudar a desacoplar efectos secundarios, pero no deben usarse como escondite de lógica. DTOs o Data Objects pueden ser útiles si el proyecto necesita pasar datos entre capas con más claridad.
Ejemplo conceptual
Antes: un método store valida campos, comprueba permisos, crea varios registros, calcula importes, aplica descuentos, envía un email, dispara una notificación, registra actividad y devuelve una respuesta personalizada. Todo funciona, pero el controller ya no coordina: ejecuta todo el caso de uso.
Después: StoreOrderRequest valida. OrderPolicy autoriza. CreateOrderAction ejecuta el caso de uso. OrderResource transforma la salida. Un Job gestiona tareas pesadas. El controller queda reducido a unas pocas líneas que se leen como una secuencia clara.
La ventaja es práctica. Si cambia la validación, sabes dónde tocar. Si cambia el caso de uso, lo pruebas sin HTTP. Si cambia la respuesta, modificas el resource. Si cambia el envío, revisas el job. Cada pieza tiene menos motivos para cambiar.
Checklist rápida
Un controller merece revisión si contiene métodos de más de 40 o 50 líneas, reglas de negocio, validaciones repetidas, queries complejas, emails o notificaciones directas, transformaciones manuales de respuesta o lógica duplicada entre endpoints.
La pregunta útil no es “¿puedo mover esto?”, sino “¿sería más claro si esta decisión viviera fuera de HTTP?”. Si puedes testear el caso de uso sin pasar por una request completa, probablemente la aplicación queda más fácil de mantener.
Cierre
Un controller grande suele ser una señal temprana de que la aplicación está perdiendo separación de responsabilidades. No siempre es urgente, pero sí conviene detectarlo antes de que cada endpoint se convierta en una pieza frágil.
Si tus controladores empiezan a parecer casos de uso completos, una Auditoría Backend Laravel puede ayudarte a detectar qué lógica conviene extraer primero y qué cambios reducirían más riesgo.
¿Tu Laravel muestra señales parecidas?
Si tu aplicación funciona, pero cada cambio empieza a ser más lento, más frágil o más difícil de explicar, una Auditoría Backend Laravel puede ayudarte a ordenar riesgos, quick wins y prioridades técnicas.





