
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
Desenvolvedor de Software
À 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.
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.
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).
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();
}
}
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']);
}
}
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'));
}
}
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.
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.
Trabalhar com intervalos de datas é uma necessidade comum em aplicações web, especialmente em sistemas que lidam com eventos, agendamentos... Leia mais
Vamos hoje desenvolver uma atividade que é comum em alguns testes de programação onde iremos desenvolver um simples jogo da... Leia mais
Para criar uma nova branch a partir da branch develop usando o terminal com o Git, siga os seguintes passos:... Leia mais
Segue uma lista das extensões que mais utilizo em meu dia a dia como desenvolvedor quando uso o editor VisualStudio... Leia mais