Mastering The Laravel App Service Pattern

Laravel PHP Service Pattern
Profile Picture Joton Sutradharโ€ข ๐Ÿ“– 6 min read โ€ข ๐Ÿ“… 27th May 2025

Heads up!

Check my blogs on Dev.to and Medium!

As your Laravel application grows, keeping your code organized becomes more important than ever. A bloated controller quickly becomes hard to read, test, and maintain. One of the best solutions to this problem is using the Service Pattern — a pattern that helps separate your business logic from your controllers.

In this blog, we'll dive deep into how to implement the Service Pattern in Laravel. We’ll walk through the folder structure, build a real-world example with custom Form Requests, and show how the service layer improves code clarity and maintainability — all without using a Repository.

๐Ÿง  What is the Service Pattern?

The Service Pattern is a design principle where you offload complex or reusable business logic into a Service class. This keeps your controllers clean and your application logic in one place, making it easier to test and reuse.

โœจ Why use the Service Pattern?

  • Separation of concerns: Controllers handle requests, services handle logic.

  • Testability: You can write unit tests for service methods easily.

  • Reusability: Use the same service in multiple places.

  • Cleaner Controllers: Easier to read, maintain, and debug

๐Ÿ“ Folder Structure

To organize your service logic effectively, here’s a commonly used folder structure:

app/
โ”œโ”€โ”€ Http/
โ”‚   โ”œโ”€โ”€ Controllers/
โ”‚   โ”‚   โ””โ”€โ”€ AuthController.php
โ”‚   โ”œโ”€โ”€ Requests/
โ”‚   โ”‚   โ””โ”€โ”€ RegisterUserRequest.php
โ”‚
โ”œโ”€โ”€ Services/
โ”‚   โ””โ”€โ”€ Auth/
โ”‚       โ””โ”€โ”€ RegisterService.php
โ”‚
โ”œโ”€โ”€ Models/
โ”‚   โ””โ”€โ”€ User.php

This structure:

  • Places each concern in its own folder

  • Makes it easier to scale and add features

  • Keeps logic modular and traceable

๐Ÿ’ก Use Case: User Registration

Let’s say we’re building a simple user registration feature. We want:

  • Validation using a custom Form Request

  • Business logic inside a Service

  • A clean, readable controller

๐Ÿงพ Step 1: Create a Custom Form Request

We’ll use Laravel’s FormRequest to handle validation cleanly.

File: app/Http/Requests/RegisterUserRequest.php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class RegisterUserRequest extends FormRequest
{
    public function authorize(): bool
    {
        // Can add authorization logic here
        return true;
    }

    public function rules(): array
    {
        return [
            'first_name' => 'required|string|max:255',
            'last_name'  => 'required|string|max:255',
            'email'      => 'required|email|unique:users,email',
            'password'   => 'required|string|min:8|confirmed',
        ];
    }
}

Here:

  • authorize() lets us control access (e.g. based on roles).

  • rules() defines the validation logic.

When you use this in a controller, Laravel auto-validates the request.

โš™๏ธ Step 2: Create the Service Class

This class will handle all the registration logic.

File: app/Services/Auth/RegisterService.php

namespace App\Services\Auth;

use App\Models\User;
use Illuminate\Support\Facades\Hash;

class RegisterService
{
    public function register(array $data): User
    {
        return User::create([
            'first_name' => $data['first_name'],
            'last_name'  => $data['last_name'],
            'email'      => $data['email'],
            'password'   => Hash::make($data['password']),
        ]);
    }
}

Now all business logic — such as password hashing, user creation — lives here. It’s reusable and easy to test.

๐ŸŽฎ Step 3: Use Service in Controller

Now, let’s call our service in the controller. This keeps the controller clean and focused on handling HTTP.

File: app/Http/Controllers/AuthController.php

namespace App\Http\Controllers;

use App\Http\Requests\RegisterUserRequest;
use App\Services\Auth\RegisterService;
use Illuminate\Http\JsonResponse;

class AuthController extends Controller
{
    protected RegisterService $registerService;

    public function __construct(RegisterService $registerService)
    {
        $this->registerService = $registerService;
    }

    public function register(RegisterUserRequest $request): JsonResponse
    {
        $user = $this->registerService->register($request->validated());

        return response()->json([
            'message' => 'User registered successfully.',
            'user'    => $user,
        ], 201);
    }
}

Let’s break this down:

  • The constructor injects the RegisterService using Laravel’s service container.

  • The register() method receives validated data from RegisterUserRequest.

  • It delegates logic to the service, and simply returns a response.

โœ… Now your controller is lean, readable, and testable.

๐Ÿงช How to Test Your Service

Because your service is decoupled from the controller, you can unit test it easily:

public function test_user_can_be_registered()
{
    $service = new RegisterService();

    $user = $service->register([
        'first_name' => 'John',
        'last_name'  => 'Doe',
        'email'      => '[email protected]',
        'password'   => 'password123',
    ]);

    $this->assertDatabaseHas('users', ['email' => '[email protected]']);
}

๐Ÿงผ Final Thoughts

The Service Pattern is a proven architectural approach in Laravel for building scalable, maintainable apps. It shines when:

  • You need to keep controllers slim

  • You want reusable business logic

  • You want to write better tests

๐Ÿ›  Summary

Component Responsibility
Controller Handles HTTP requests/responses
Form Request Handles validation
Service Handles business logic (e.g. registration)
Model Represents database entity
Related Blogs
Supercharge Your Laravel App With Queues โ€“ A Developerโ€™s Experience With Background Email Sending
Laravel Jobs Queue Queue Jobs Php Joton Sutradhar

As Laravel developers, one of the critical lessons we eventually learn is: not everything should happen in real-time. Whether it's sending emails, processing images, syncing third-party data, or running analytics โ€” pushing these resource-heavy or time-consuming tasks to the background is essential for a performant and responsive application.

Profile Picture Joton Sutradhar โ€ข ๐Ÿ“– 6 min read โ€ข ๐Ÿ“… 29th May 2025
Integrating Meilisearch With Laravel Using Docker
Laravel Meilisearch Docker Integrating Laravel & Meilisearch

If you're building a Laravel application and want blazing-fast search capabilities, Meilisearch is one of the best tools you can integrate. In this post, weโ€™ll explore what Meilisearch is, why you should consider it, its use cases, pros and cons, and how to integrate it into a Laravel project using Docker.

Profile Picture Joton Sutradhar โ€ข ๐Ÿ“– 5 min read โ€ข ๐Ÿ“… 26th May 2025
Standard Logging Management In A Laravel Application
Logging Laravel Log Management

Effective logging is critical in any Laravel application โ€” whether you're debugging issues in development or monitoring your app in production. Laravel, being a modern PHP framework, provides a robust logging system powered by Monolog, giving you flexibility, power, and control.

Profile Picture Joton Sutradhar โ€ข ๐Ÿ“– 4 min read โ€ข ๐Ÿ“… 24th May 2025
Subscribe to my newsletter

Get recent projects & blog updates to your inbox.

I never share your email. Read our privacy policy.

© 2025 Joton Sutradhar. All rights reserved.