PauloRB.dev

Desenvolvedor de Software

Usando Strategy e Pipeline juntos no Laravel para organizar regras complexas | PauloRB.dev Usando Strategy e Pipeline juntos no Laravel para organizar regras complexas – PauloRB.dev
Usando Strategy e Pipeline juntos no Laravel para organizar regras complexas

Usando Strategy e Pipeline juntos no Laravel para organizar regras complexas

À medida que um sistema cresce, a lógica de negócio costuma se tornar mais complexa. Um dia você está apenas filtrando dados. No outro, está classificando, ordenando, eliminando, aplicando exceções e resolvendo empates. É aí que os padrões de projeto se tornam aliados poderosos para manter o código limpo e sustentável.

Dois desses padrões se complementam muito bem: Strategy e Pipeline.

Neste artigo, vamos explorar como esses dois padrões podem ser usados em conjunto no Laravel para organizar fluxos complexos de regras, como recomendações personalizadas, motores de classificação ou processamento condicional em múltiplas etapas.

Por que usar Strategy e Pipeline juntos?

Antes de ir para o código, é importante entender os papéis de cada padrão:

  • Strategy: isola o modo de executar uma tarefa. Você pode ter diferentes estratégias para resolver o mesmo problema (ex: diferentes formas de recomendar, calcular, ou classificar). Se você quer conhecer um pouco mais sobre o padrão Strategy pode acessar Artigo : Padrão de Projeto Strategy: Flexibilidade e Eficiência no Design de Software com PHP

  • Pipeline: permite aplicar várias operações sequenciais sobre um dado. Você define um conjunto de etapas e aplica uma a uma.

Quando combinados, o Strategy define a lógica geral — qual pipeline será usado — e o Pipeline define a execução passo a passo das regras envolvidas.

Um exemplo prático: recomendação de cursos

Imagine que você está construindo um sistema que recomenda cursos para usuários com base em diversos critérios:

  • O nível do usuário (iniciante, intermediário, avançado)

  • O tempo que ele tem disponível

  • Seus temas de interesse

  • A popularidade dos cursos

A regra pode variar entre usuários, e você pode ter diferentes formas de recomendação (ex: simples, personalizada, baseada em histórico).

Strategy: isolando os modos de recomendação

A primeira parte é criar uma interface comum para todas as estratégias de recomendação:

namespace App\Services\Recomendacao\Contracts;

interface RecomendacaoStrategy
{
    public function recomendar(array $cursos, array $usuario): array;
}

Em seguida, uma estratégia concreta personalizada, que vai usar o pipeline para executar as regras:

namespace App\Services\Recomendacao\Estrategias;

use App\Services\Recomendacao\Contracts\RecomendacaoStrategy;
use App\Services\Recomendacao\Recomendador;
use App\Services\Recomendacao\Pipelines\FiltrarPorNivel;
use App\Services\Recomendacao\Pipelines\FiltrarPorTempoDisponivel;
use App\Services\Recomendacao\Pipelines\PriorizarInteresses;
use App\Services\Recomendacao\Pipelines\OrdenarPorPopularidade;

class RecomendacaoPersonalizada implements RecomendacaoStrategy
{
    public function __construct(
        protected Recomendador $recomendador,
        protected FiltrarPorNivel $filtroNivel,
        protected FiltrarPorTempoDisponivel $filtroTempo,
        protected PriorizarInteresses $priorizarInteresses,
        protected OrdenarPorPopularidade $ordenarPopularidade,
    ) {}

    public function recomendar(array $cursos, array $usuario): array
    {
        return $this->recomendador
            ->com($cursos)
            ->paraUsuario($usuario)
            ->passandoPor([
                $this->filtroNivel,
                $this->filtroTempo,
                $this->priorizarInteresses,
                $this->ordenarPopularidade,
            ])
            ->executar();
    }
}

Pipeline: executando regras em sequência

O Pipeline é quem executa as transformações, uma a uma, na ordem definida:

namespace App\Services\Recomendacao;

class Recomendador
{
    protected array $cursos = [];
    protected array $usuario = [];
    protected array $etapas = [];

    public function com(array $cursos): self
    {
        $this->cursos = $cursos;
        return $this;
    }

    public function paraUsuario(array $usuario): self
    {
        $this->usuario = $usuario;
        return $this;
    }

    public function passandoPor(array $etapas): self
    {
        $this->etapas = $etapas;
        return $this;
    }

    public function executar(): array
    {
        return array_reduce(
            $this->etapas,
            fn($cursos, $etapa) => $etapa->handle($cursos, $this->usuario),
            $this->cursos
        );
    }
}

Cada etapa é uma classe separada, fácil de testar, como neste exemplo:

namespace App\Services\Recomendacao\Pipelines;

class FiltrarPorNivel
{
    public function handle(array $cursos, array $usuario): array
    {
        return array_filter($cursos, fn($curso) => $curso['nivel'] === $usuario['nivel']);
    }
}

Resolvendo tudo via Service Container

No Laravel, é uma boa prática registrar a estratégia padrão no container. Assim, você injeta a interface no controller, e o Laravel cuida do resto:

// App\Providers\AppServiceProvider.php

use App\Services\Recomendacao\Contracts\RecomendacaoStrategy;
use App\Services\Recomendacao\Estrategias\RecomendacaoPersonalizada;

public function register()
{
    $this->app->bind(RecomendacaoStrategy::class, function () {
        return app(RecomendacaoPersonalizada::class);
    });
}

No Controller: simples e limpo

use App\Services\Recomendacao\Contracts\RecomendacaoStrategy;

class CursoController extends Controller
{
    public function recomendar(RecomendacaoStrategy $estrategia)
    {
        $usuario = auth()->user()->toArray();
        $cursos = Curso::all()->toArray();

        $recomendados = $estrategia->recomendar($cursos, $usuario);

        return view('cursos.recomendados', compact('recomendados'));
    }
}

Vantagens reais da combinação Strategy + Pipeline

Ao combinar Strategy com Pipeline, você ganha:

Separação clara de responsabilidades
Strategy define o que fazer, Pipeline define como fazer passo a passo.

Extensibilidade
Você pode adicionar novas estratégias ou novas etapas sem quebrar nada.

Testabilidade
Cada etapa pode ser testada de forma isolada.

Injeção automática
A estratégia e suas dependências são resolvidas pelo container do Laravel.

Conclusão

Combinar Strategy e Pipeline é uma forma poderosa de lidar com lógicas complexas e altamente variáveis em sistemas Laravel. Com Strategy, você pode trocar facilmente o comportamento global. Com Pipeline, você define fluxos reutilizáveis, testáveis e organizados.

Se você trabalha com regras que envolvem múltiplas etapas — como classificação, recomendação, filtragem ou processamento — essa abordagem pode deixar seu código muito mais limpo e preparado para o crescimento.

Mais Posts

Variáveis Dinâmicas e Referências no PHP

Variáveis Dinâmicas e Referências no PHP

No mundo da programação PHP, compreender o conceito de variáveis de variáveis e o uso de referências é fundamental para... Leia mais

Primeiros Passos com Go: Criando uma Aplicação Interativa do Zero

Primeiros Passos com Go: Criando uma Aplicação Interativa do Zero

Se você está começando a aprender Go e quer fazer algo prático, vou te mostrar como criar uma aplicação básica... Leia mais

Rascunho para estudo de programação orientada a objetos em Python

Rascunho para estudo de programação orientada a objetos em Python

A orientação a objetos (OO) é um paradigma de programação central em Python, que facilita a organização e estruturação do... Leia mais

Design Pattern Facade em PHP : Exemplo prático

Design Pattern Facade em PHP : Exemplo prático

O Design Pattern Facade é uma solução elegante para simplificar a interface de um subsistema complexo, tornando-o mais fácil de... Leia mais