El error que comete casi todo el mundo

Elegir el framework antes de entender el problema. O peor: usar siempre el mismo porque «ya lo conocemos». He visto proyectos de gestión interna construidos sobre Magento porque el equipo solo sabía Magento, y he visto tiendas con cientos de referencias construidas en Laravel porque «es más moderno». En ambos casos, el resultado fue el mismo: meses de trabajo para replicar funcionalidades que el framework equivocado no trae de serie.

La pregunta correcta no es «¿qué framework usamos?» sino «¿qué necesita realmente este proyecto?». Después el framework es una consecuencia.

Cuándo Laravel es la respuesta obvia

Laravel brilla cuando el núcleo del proyecto es lógica de negocio propia: reglas complejas, flujos de trabajo, integraciones con APIs externas o procesamiento asíncrono. Situaciones concretas en las que lo elegiría sin dudar:

  • Panel de administración o backoffice a medida (no un eCommerce estándar).
  • API REST que va a consumir una app móvil o un frontend desacoplado.
  • Integración con ERPs, pasarelas de pago propias o sistemas legacy.
  • Procesamiento en background: importaciones masivas, envíos de email, sincronizaciones periódicas.
  • Proyectos donde el modelo de datos es complejo y evoluciona rápido.

Eloquent y migraciones: el modelo de datos como código

Una de las ventajas más prácticas de Laravel es que el esquema de base de datos vive en el repositorio. Crear un modelo con su migración:

php artisan make:model Order -mcs
# -m crea la migration
# -c crea el Controller
# -s crea el Seeder

La migración generada se convierte en el contrato del esquema:

Schema::create('orders', function (Blueprint $table) {
    $table->id();
    $table->foreignId('customer_id')->constrained();
    $table->string('reference')->unique();
    $table->decimal('total', 10, 2);
    $table->enum('status', ['pending', 'paid', 'shipped', 'cancelled'])
          ->default('pending');
    $table->json('metadata')->nullable();
    $table->timestamps();
});

Y las relaciones en el modelo son legibles sin necesidad de documentación adicional:

class Order extends Model
{
    protected $casts = [
        'metadata' => 'array',
        'status'   => OrderStatus::class, // enum PHP 8.1
    ];

    public function customer(): BelongsTo
    {
        return $this->belongsTo(Customer::class);
    }

    public function lines(): HasMany
    {
        return $this->hasMany(OrderLine::class);
    }

    public function scopePending(Builder $query): Builder
    {
        return $query->where('status', OrderStatus::Pending);
    }
}

Procesamiento asíncrono con Jobs

Uno de los patrones que más valor aporta en proyectos reales es mover el trabajo pesado fuera del ciclo de petición-respuesta. En Laravel esto es trivial:

// Despachar el job desde el controlador — respuesta inmediata al usuario
public function import(Request $request): JsonResponse
{
    $path = $request->file('csv')->store('imports');

    ImportOrdersCsv::dispatch($path, auth()->id())
        ->onQueue('imports')
        ->delay(now()->addSeconds(5));

    return response()->json(['message' => 'Importación en proceso']);
}

// El job se ejecuta en background sin bloquear
class ImportOrdersCsv implements ShouldQueue
{
    use Dispatchable, Queueable;

    public int $tries = 3;
    public int $timeout = 300;

    public function handle(OrderImporter $importer): void
    {
        $importer->fromCsv($this->path, $this->userId);
    }
}

Montar esto mismo desde cero en PHP plano, o adaptarlo a la arquitectura de Magento para un caso que no es eCommerce, costaría semanas.

Cuándo Laravel es demasiado

Laravel no es la respuesta a todo. Hay contextos donde añade complejidad sin valor:

  • Scripts puntuales o CLI tools simples. Para un script de migración de datos que se ejecuta una vez, un PHP plano con PDO es más que suficiente. Montar un proyecto Laravel completo para eso es sobreingeniería.
  • Microservicios muy ligeros. Si el servicio hace una sola cosa (por ejemplo, redimensionar imágenes o convertir monedas), un micro-framework como Slim o incluso un script con un servidor embebido puede ser más apropiado.
  • Equipos que no lo conocen y el plazo no da margen. Laravel tiene una curva de aprendizaje real. Si el equipo viene de Symfony o de otro stack y el proyecto tiene fecha fija, la productividad inicial será baja.

Cuándo el eCommerce manda: Magento primero

Esta tensión la vivo constantemente: un cliente con una tienda mediana que necesita funcionalidad extra. ¿Lo hacemos en Laravel o extendemos Magento?

La respuesta depende de si la funcionalidad es parte del catálogo/checkout o es un sistema auxiliar. Magento tiene un sistema de módulos propio, con observers, plugins y preferencias que permiten modificar cualquier comportamiento sin tocar el core:

<!-- Magento/etc/events.xml -->
<config>
    <event name="sales_order_place_after">
        <observer name="my_module_sync_erp"
                  instance="MyCompany\MyModule\Observer\SyncOrderToErp"/>
    </event>
</config>
// El observer equivalente en Laravel sería un Event Listener
// Magento: más verboso, pero integrado con el ciclo de vida del pedido
class SyncOrderToErp implements ObserverInterface
{
    public function execute(Observer $observer): void
    {
        $order = $observer->getEvent()->getOrder();

        // Aquí la lógica de sincronización con el ERP
        $this->erpClient->pushOrder([
            'reference' => $order->getIncrementId(),
            'total'     => $order->getGrandTotal(),
            'items'     => $this->mapItems($order->getAllItems()),
        ]);
    }
}

Si la funcionalidad está ligada al pedido, al catálogo o al cliente de Magento, es más barato y más mantenible extender Magento que sincronizar datos entre dos sistemas. Cuando la funcionalidad es genuinamente independiente —un portal de proveedores, un sistema de gestión de devoluciones con flujo de aprobaciones, un dashboard de analítica— ahí sí tiene sentido un proyecto Laravel separado que consuma la API de Magento.

Las cinco preguntas que hago antes de elegir stack

Con los años he reducido el proceso de decisión a cinco preguntas. Si tengo respuesta clara para todas, la elección de framework sale sola:

  1. ¿El núcleo del negocio es un catálogo de productos con carrito y checkout? → Magento (o plataforma eCommerce establecida). No reinventes la rueda.
  2. ¿La lógica de negocio es compleja y va a cambiar con frecuencia? → Laravel. Su arquitectura facilita el cambio sin romper lo que ya funciona.
  3. ¿Hay procesamiento en background, colas o tareas programadas? → Laravel. Horizon + Scheduler está resuelto de serie.
  4. ¿Es una integración puntual o un script de ciclo de vida corto? → PHP plano o Artisan command si ya hay un proyecto Laravel cerca.
  5. ¿El equipo lo va a mantener a largo plazo? → El framework que el equipo ya conoce y puede mantener sin depender de un único desarrollador.

Conclusión

No existe un framework universalmente correcto. Lo que sí existe es un proceso para llegar a la decisión correcta: entender el problema antes de abrir el terminal, evaluar lo que el proyecto necesita de serie vs lo que habría que construir, y ser honesto sobre las capacidades del equipo que lo va a mantener.

En mi caso, Laravel es la elección por defecto para cualquier proyecto que no sea eCommerce estándar. Pero «por defecto» no significa «siempre»: hay proyectos donde la mejor solución es un módulo de Magento bien hecho, y otros donde ni siquiera hace falta un framework.

Si estás evaluando el stack para un proyecto y quieres una segunda opinión, cuéntame en qué consiste.