Refactorizando backends Laravel legacy con Pipelines y Actions: de controladores gordos a flujos mantenibles
Cómo usar Pipelines, Actions y Jobs en Laravel para transformar controladores monolíticos en flujos desacoplados, testeables y listos para escalar en producción.
Refactorizando backends Laravel legacy con Pipelines y Actions: de controladores gordos a flujos mantenibles
Muchos proyectos Laravel en producción arrancaron como MVPs y terminaron con controladores enormes que mezclan validación, dominio, integración con terceros y side effects, volviendo doloroso cualquier cambio nuevo.
El problema de los controladores gordos en Laravel
Un patrón clásico en proyectos legacy es encontrar métodos de controlador de 300+ líneas que hacen desde validar el request hasta disparar notificaciones, escribir logs y hablar con servicios externos, todo en un único flujo acoplado.
Este enfoque rompe el principio de responsabilidad única, dificulta el testing aislado y complica tareas de observabilidad y feature flags en producción.
1. Presentando Pipelines y Actions como capa de orquestación
Qué es un Pipeline en Laravel
El componente Illuminate\Pipeline\Pipeline permite modelar un flujo de pasos donde cada etapa recibe un payload, lo transforma y lo pasa a la siguiente, ideal para orquestar casos de uso complejos sin inflar el controlador.
// app/Support/Pipelines/CreateOrderPipeline.php
use Illuminate\Pipeline\Pipeline;
class CreateOrderPipeline
{
public function __construct(private Pipeline $pipeline) {}
public function handle(array $payload): array
{
return $this->pipeline
->send($payload)
->through([
\App\Actions\Orders\ValidateOrderData::class,
\App\Actions\Orders\CalculateTotals::class,
\App\Actions\Orders\PersistOrder::class,
\App\Actions\Orders\DispatchPostCreateJobs::class,
])
->thenReturn();
}
}
Cada Action encapsula una responsabilidad concreta y es fácilmente testeable, lo que reduce el riesgo de regresiones en entornos con alto tráfico.
2. Extrayendo lógica de un controlador legacy
Antes: controlador monolítico
// app/Http/Controllers/OrderController.php
public function store(Request $request)
{
$data = $request->validate([
'items' => 'required|array|min:1',
'customer_id' => 'required|exists:customers,id',
]);
// cientos de líneas con lógica de negocio, cálculos, integraciones...
}
Después: controlador delgado + Pipeline
public function store(Request $request, CreateOrderPipeline $pipeline)
{
$payload = $request->all();
$result = $pipeline->handle($payload);
return response()->json([
'order_id' => $result['order']->id,
'status' => 'created',
], 201);
}
El controlador apenas coordina el input/output HTTP, mientras que la lógica de negocio vive en Actions autocontenidas reutilizables incluso desde Jobs o comandos Artisan.
3. Actions autocontenidas y testeables
Ejemplo de Action de dominio
// app/Actions/Orders/CalculateTotals.php
namespace App\Actions\Orders;
class CalculateTotals
{
public function __invoke(array $payload): array
{
$subtotal = collect($payload['items'])
->sum(fn ($item) => $item['price'] * $item['qty']);
$payload['subtotal'] = $subtotal;
$payload['total'] = $subtotal; // aplicar impuestos, descuentos, etc.
return $payload;
}
}
Este diseño permite tests unitarios de cada etapa sin bootstrapping completo de Laravel, acelerando CI y mejorando la calidad del dominio.
4. Integración con Jobs, colas y observabilidad
Separando side effects en Jobs
Las Actions de orquestación pueden delegar side effects pesados a Jobs encolados, como integración con gateways de pago o notificaciones.
// app/Actions/Orders/DispatchPostCreateJobs.php
namespace App\Actions\Orders;
use App\Jobs\SendOrderConfirmation;
class DispatchPostCreateJobs
{
public function __invoke(array $payload): array
{
dispatch(new SendOrderConfirmation($payload['order']));
return $payload;
}
}
Esto facilita aplicar retries, timeouts y circuit breakers a nivel de infraestructura (Horizon, Redis, supervisores) sin acoplar esa lógica al controlador.
5. Beneficios en producción y DevOps
- Controladores livianos que reducen conflictos en merges y facilitan code review.
- Flujos de negocio expresados como Pipelines fácilmente observables en logs y traces.
- Tests más rápidos y fiables, cruciales para pipelines de CI/CD frecuentes.
- Capacidad de activar/desactivar etapas con feature flags sin tocar el endpoint HTTP.
6. Extensión hacia arquitectura limpia
La combinación de Controllers delgados + Pipelines + Actions es un primer paso hacia arquitecturas más limpias (hexagonal, DDD light) sin reescribir todo el proyecto de cero.
Sobre esta base, se pueden introducir puertos/adaptadores, repositorios explícitos y políticas de validación consistentes en todo el backend Laravel.
Conclusión
Refactorizar un backend Laravel legacy no requiere un big bang; requiere introducir patrones como Pipelines y Actions que permitan dividir el problema en pasos manejables.
Adoptar estos patrones mejora la mantenibilidad, reduce el riesgo en producción y sienta las bases para evolucionar el proyecto hacia una arquitectura moderna preparada para escalar.
artículos_relacionados
FastAPI + Laravel: Microservicios Híbridos con Python y PHP
Construye microservicios donde FastAPI maneja APIs async/IA y Laravel orquesta workflows. Integra ambos con ejemplos reales de alto rendimiento.
PostgreSQL vs MongoDB en Laravel: Elegir la Base de Datos Correcta
Análisis comparativo detallado de PostgreSQL y MongoDB para aplicaciones Laravel, evaluando rendimiento, escalabilidad, complejidad y casos de uso ideales para cada tecnología.