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

Como Usar Between Entre Duas Colunas de Data no Laravel

Como Usar Between Entre Duas Colunas de Data no Laravel

Trabalhar com intervalos de datas é uma necessidade comum em aplicações web, especialmente em sistemas que lidam com eventos, agendamentos... Leia mais

Jogo da Velha com HTML, CSS e JavaScript

Jogo da Velha com HTML, CSS e JavaScript

Vamos hoje desenvolver uma atividade que é comum em alguns testes de programação onde iremos desenvolver um simples jogo da... Leia mais

Criando uma nova branch a partir da develop pelo terminal com GIT

Criando uma nova branch a partir da develop pelo terminal com GIT

Para criar uma nova branch a partir da branch develop usando o terminal com o Git, siga os seguintes passos:... Leia mais

Extensões Visual Studio Code para o dia a dia

Extensões Visual Studio Code para o dia a dia

Segue uma lista das extensões que mais utilizo em meu dia a dia como desenvolvedor quando uso o editor VisualStudio... Leia mais