Patrones de Diseño que Sí Usarás en Laravel: Repository, Strategy, Factory y Action Classes
Los patrones de diseño suelen enseñarse como conceptos abstractos llenos de diagramas UML y ejemplos artificiales. El problema es que muchos desarrolladores terminan aplicándolos incorrectamente o, peor aún, introduciendo capas innecesarias que complican aplicaciones simples.
En el ecosistema Laravel esto ocurre constantemente. Es común encontrar proyectos pequeños con arquitecturas sobrediseñadas donde cada modelo tiene:
Repository.
Interface.
Service.
Manager.
Transformer.
DTO.
Facade personalizada.
Todo para ejecutar un simple CRUD.
Desde mi experiencia, los patrones de diseño son herramientas extremadamente útiles, pero solamente cuando resuelven problemas reales. Si se utilizan únicamente porque 'así debe hacerse', terminan destruyendo la mantenibilidad del proyecto.
El mejor patrón de diseño no es el más sofisticado. Es el que reduce complejidad en lugar de crearla.
El problema de copiar arquitecturas empresariales
Uno de los mayores errores en Laravel es copiar arquitecturas diseñadas para sistemas enormes dentro de aplicaciones pequeñas.
Laravel ya ofrece muchas abstracciones poderosas:
Container.
Eloquent.
Policies.
Events.
Jobs.
Resources.
Agregar capas adicionales sin necesidad puede terminar generando:
Más archivos.
Más acoplamiento.
Más complejidad mental.
Menor velocidad de desarrollo.
La clave está en aplicar patrones únicamente cuando existe una necesidad clara.
Repository Pattern: cuándo sí tiene sentido
El Repository Pattern probablemente sea el patrón más debatido dentro del ecosistema Laravel.
Muchas personas crean repositories simplemente porque:
UserRepository
PostRepository
OrderRepository
sin tener realmente un motivo arquitectónico válido.
La realidad es que Eloquent ya funciona parcialmente como un repository.
Por eso, en aplicaciones pequeñas o medianas, agregar repositories puede ser redundante.
Cuándo SÍ usar Repository
Considero que Repository empieza a ser útil cuando:
Existen múltiples fuentes de datos.
Hay lógica compleja de consultas reutilizables.
Se requiere desacoplar infraestructura.
El dominio crece considerablemente.
Por ejemplo:
class OrderRepository
{
public function getPendingInvoices()
{
return Order::query()
->where('status', 'pending')
->whereDate('due_date', '<', now())
->with('customer')
->get();
}
}
Aquí el repository encapsula consultas complejas reutilizables.
Cuándo NO usar Repository
Si el repository simplemente replica Eloquent:
public function find($id)
{
return User::find($id);
}
entonces probablemente no está aportando valor.
Muchas veces solamente agrega una capa extra innecesaria.
Strategy Pattern: perfecto para lógica variable
El Strategy Pattern es uno de los patrones más útiles y prácticos en Laravel.
Funciona especialmente bien cuando una misma acción puede ejecutarse de distintas formas.
Por ejemplo:
Métodos de pago.
Sistemas de envío.
Procesadores de archivos.
Algoritmos de descuento.
Ejemplo:
interface PaymentStrategy
{
public function pay(float $amount);
}
Estrategias concretas:
class StripePayment implements PaymentStrategy
{
public function pay(float $amount)
{
// lógica Stripe
}
}
class PaypalPayment implements PaymentStrategy
{
public function pay(float $amount)
{
// lógica Paypal
}
}
Y luego:
class PaymentService
{
public function __construct(
protected PaymentStrategy $strategy
) {}
}
Esto evita enormes bloques de:
if ($provider === 'stripe') {}
elseif ($provider === 'paypal') {}
El código se vuelve mucho más mantenible y extensible.
Factory Pattern: más útil de lo que parece
Muchas personas asocian Factory únicamente con model factories para testing, pero el patrón Factory tiene aplicaciones mucho más amplias.
Una Factory es especialmente útil cuando la creación de objetos depende de contexto dinámico.
Por ejemplo:
class ReportFactory
{
public static function make(string $type)
{
return match($type) {
'sales' => new SalesReport(),
'finance' => new FinanceReport(),
'inventory' => new InventoryReport(),
};
}
}
Esto centraliza la creación de objetos y evita lógica dispersa por toda la aplicación.
En Laravel, las factories también funcionan muy bien para:
Drivers.
Integraciones externas.
Generadores.
Pipelines.
Exportadores.
Action Classes: probablemente el patrón más práctico en Laravel moderno
Uno de los enfoques que más utilizo actualmente son las Action Classes.
Laravel fomenta controllers ligeros, pero muchas veces la lógica termina migrándose a services gigantes.
Las Action Classes ofrecen una alternativa extremadamente limpia.
Por ejemplo:
class CreateUserAction
{
public function execute(array $data): User
{
return User::create($data);
}
}
Luego:
public function store(Request $request)
{
$user = $this->createUserAction
->execute($request->validated());
}
Este enfoque aporta varias ventajas:
Responsabilidad única.
Alta reutilización.
Testing sencillo.
Controllers pequeños.
Lógica desacoplada.
El peligro de los Service gigantes
Muchos proyectos empiezan correctamente pero terminan creando:
UserService
con miles de líneas y decenas de métodos.
Esto eventualmente se convierte en un nuevo problema arquitectónico.
Las Action Classes ayudan precisamente a dividir comportamiento en unidades pequeñas y enfocadas.
Patrones y testing
Uno de los beneficios reales de los patrones aparece cuando la aplicación empieza a crecer y requiere testing más sólido.
Por ejemplo, Strategy facilita mocks:
$this->mock(PaymentStrategy::class);
Action Classes permiten tests unitarios extremadamente claros:
it('creates a user correctly');
Repository puede simplificar testing de acceso a datos en ciertos escenarios.
Sin embargo, es importante entender que los patrones no existen únicamente para testing. Si una arquitectura se vuelve incómoda solamente para facilitar mocks, probablemente esté sobrediseñada.
Cuándo los patrones empiezan a ayudar realmente
En mi experiencia, los patrones empiezan a aportar verdadero valor cuando:
La lógica crece.
Existen múltiples reglas de negocio.
Hay varios desarrolladores.
El sistema evoluciona constantemente.
Existen integraciones externas.
Antes de eso, muchas veces la simplicidad es más valiosa.
La obsesión por Clean Architecture
Actualmente existe cierta tendencia a convertir cualquier proyecto Laravel en una implementación extrema de Clean Architecture.
Eso puede funcionar en sistemas empresariales enormes, pero en muchos proyectos produce:
Demasiadas carpetas.
Navegación complicada.
Curva de aprendizaje alta.
Desarrollo más lento.
Laravel ya ofrece una arquitectura muy productiva. La meta debería ser mejorarla, no luchar contra ella.
Mi enfoque práctico en Laravel
Actualmente, mi enfoque suele ser:
Controllers: coordinación HTTP.
Action Classes: lógica puntual.
Strategy: comportamiento variable.
Repository: solamente cuando existe complejidad real de datos.
Factories: creación dinámica de objetos.
Intento evitar capas adicionales mientras el sistema no las necesite realmente.
El verdadero objetivo de los patrones
Muchas veces los patrones se enseñan como si fueran reglas obligatorias.
Pero el objetivo real siempre debería ser:
Reducir complejidad.
Mejorar legibilidad.
Facilitar mantenimiento.
Permitir evolución.
Si un patrón no mejora alguno de esos aspectos, probablemente no debería existir dentro del proyecto.
Conclusión
Los patrones de diseño siguen siendo herramientas extremadamente valiosas en Laravel, pero solamente cuando se aplican con criterio.
Repository, Strategy, Factory y Action Classes pueden transformar enormemente una aplicación cuando resuelven problemas reales.
Sin embargo, aplicarlos indiscriminadamente puede generar exactamente el efecto contrario: más complejidad, más acoplamiento y menor productividad.
La mejor arquitectura no es la más académica. Es la que permite que el equipo construya software mantenible, claro y escalable sin convertir cada feature en un laberinto de abstracciones.