Back to Engineering Notes
Laravel ConceptsEngineering Note

13. Service Layer + Repository Pattern

This is one of my backend architecture approaches, especially when I build medium to large Laravel applications, to keep the system clean, scalable, and maintainable.

🧠 Service Layer + Repository Pattern

> This is one of my backend architecture approaches, especially when I build medium to large Laravel applications, to keep the system clean, scalable, and maintainable.

plain text
Route
 ↓
Controller
 ↓ injects
Service
 ↓ uses Trait
Service
 ↓ injects
Repository
 ↓
Model
 ↓
Database

🎯 Simple Idea

Controller → receives request and returns response
Service (Service Layer) → handles business logic
Repository (Repository Pattern) → handles database queries
Model → represents database table
View → displays data (if needed)
Trait → reusable logic inside Service classes
Dependency Injection → automatically provides required classes

👉 I separate responsibilities clearly so each layer has one focused job


🧩 How the Flow Works

Client sends request
Controller receives and validates it
Controller calls Service (via Dependency Injection)
Service handles business logic
can reuse shared logic via Traits
Service calls Repository (via Dependency Injection)
Repository interacts with Model / Database
Data flows back up → Service → Controller → Response

🧩 Full Flow

plain text
User Request
 ↓
Route
 ↓
Controller
 ↓ Dependency Injection
UserService
 ↓ uses Trait (shared logic)
UserRepository
 ↓
User Model
 ↓
Database

🧱 Example Flow

Controller injects Service

plain text
class UserController extends Controller
{
    public function __construct(
        protected UserService $userService
    ) {}

    public function store(StoreUserRequest $request)
    {
        return $this->userService->createUser($request->validated());
    }
}

Service injects Repository and uses Trait

plain text
class UserService
{
    use UploadFileTrait;

    public function __construct(
        protected UserRepository $userRepository
    ) {}

    public function createUser(array $data)
    {
        if (isset($data['avatar'])) {
            $data['avatar'] = $this->uploadFile($data['avatar']);
        }

        return $this->userRepository->create($data);
    }
}

Trait used inside Service

plain text
trait UploadFileTrait
{
    public function uploadFile($file)
    {
        return $file->store('uploads');
    }
}

Repository handles database logic

plain text
class UserRepository
{
    public function create(array $data)
    {
        return User::create($data);
    }
}

📌 Dependency Injection Flow

plain text
Controller needs UserService
→ Laravel Service Container resolves UserService

UserService needs UserRepository
→ Laravel Service Container resolves UserRepository

UserRepository uses User Model
→ Model interacts with Database

👉 I rely on Laravel’s Service Container to automatically resolve dependencies, keeping the code clean and loosely coupled.


🧠 Why I Use This Approach

keeps controllers thin and focused
separates business logic from data access
improves code reusability (via services & traits)
makes the system easy to test (mock services/repositories)
supports scalability for medium to large applications

⚖️ Trade-offs

introduces more layers (more files/classes)
may be over-engineered for small/simple CRUD apps
requires consistent structure and discipline

📌 Practical Rule

plain text
Controller → request/response only (thin)
Service → business logic (core layer)
Trait → reusable service logic
Repository → database queries only
Model → data structure & relationships
View → presentation layer

💬 Summary

This is one of my backend architecture approaches, especially for medium to large Laravel applications, where I apply a Service Layer with Repository Pattern, combined with Dependency Injection and Traits.

plain text
Request
→ Controller
→ Service
→ Trait (if needed)
→ Repository
→ Model
→ Database

👉 This allows me to build systems that are clean, maintainable, testable, and scalable for production