Sometimes a Service or Action class grows because it performs multiple sequential steps. For example, when onboarding a new user, you might need to: generate a profile photo, create demo data, activate a trial subscription, and send a welcome email.
You could put all of that in one method, but there's a more structural alternative: Pipelines.
Laravel's Pipeline facade lets you send data through a series of "pipe" classes, each performing one step. It's the same mechanism Laravel uses internally for middleware.
Pipelines are not a replacement for Services or Actions. They're an alternative for multi-step sequential operations where each step is its own class.
How Pipelines Work
The basic Pipeline syntax:
use Illuminate\Support\Facades\Pipeline; $user = Pipeline::send($user) ->through([ GenerateProfilePhoto::class, CreateDemoProject::class, ActivateTrialSubscription::class, SendWelcomeEmail::class, ]) ->thenReturn();
Each "pipe" class receives the data, does its work, and passes it to the next pipe. Here's what one looks like:
app/Pipes/GenerateProfilePhoto.php:
namespace App\Pipes; use App\Models\User;use Closure; class GenerateProfilePhoto{ public function handle(User $user, Closure $next) { $user->update([ 'avatar' => 'https://ui-avatars.com/api/?name=' . urlencode($user->name), ]); return $next($user); }}
app/Pipes/CreateDemoProject.php:
namespace App\Pipes; use App\Models\User;use App\Models\Project;use Closure; class CreateDemoProject{ public function handle(User $user, Closure $next) { Project::create([ 'user_id' => $user->id, 'name' => 'Demo project 1', ]); return $next($user); }}
The pattern is always the same: accept the data, do your work, call $next($data).
Using Pipelines in the Controller
In the Controller, you would replace a long Service method with...