Teknoloji

Laravel ile Generic Service Yapısı

📅 Jan 31, 2026
Laravel projelerinde iş mantığını controller’lardan ayırmak, kod tekrarını azaltmak ve sürdürülebilir bir mimari oluşturmak için Generic Service yaklaşımı oldukça güçlü bir çözümdür.

Bu yapı sayesinde CRUD işlemleri, listeleme, güncelleme ve silme gibi ortak işlevler tek bir merkezden yönetilir ve her model için tekrar tekrar yazılmasına gerek kalmaz.

🎯 Neden Generic Service?

  • Kod tekrarını minimuma indirir
  • Controller’ları sade ve okunabilir tutar
  • İş mantığını tek bir katmanda toplar
  • Yeni modeller eklerken hızlı geliştirme sağlar
  • Test edilebilir ve genişletilebilir bir yapı sunar

🏗️ Mimari Yaklaşım

Bu mimari 3 temel parçadan oluşur:

  1. Service Interface
  2. Base (Generic) Service
  3. Model’e Özel Service Sınıfları

📌 Service Interface (Sözleşme Katmanı)

ServiceInterface, tüm servislerin uyması gereken kuralları tanımlar.
Bu sayede her servis:

  • Aynı method imzalarına sahip olur
  • Tutarlı bir API sunar
  • Bağımlılıkların kolayca değiştirilebilmesini sağlar

Interface üzerinde Generic Type (Template) kullanılarak:

  • Model
  • Create DTO
  • Update DTO
  • List DTO

tipleri dinamik hale getirilmiştir.

Bu yaklaşım sayesinde IDE desteği artar ve tip güvenliği sağlanır.

⚙️ BaseService (Generic Servis)

BaseService, tüm servislerin ortak kullandığı çekirdek iş mantığını barındırır.

Burada tanımlanan işlemler:

  • all() → Listeleme & pagination
  • create() → Kayıt oluşturma
  • show() → Tekil kayıt getirme
  • update() → Güncelleme
  • delete() → Silme

Tüm bu işlemler:

  • DTO üzerinden gelen verilerle çalışır
  • Eloquent Model bağımlılığı ile yönetilir
  • Tekrar yazılmadan tüm modellere uygulanabilir

Bu sayede her model için standart CRUD işlemleri otomatik olarak hazır gelir.

📦 DTO (Data Transfer Object) Kullanımı

Service katmanında doğrudan Request yerine DTO kullanılması:

  • Validation sonrası temiz veri akışı sağlar
  • Katmanlar arası bağımlılığı azaltır
  • Service katmanını framework’ten bağımsız hale getirir
  • Test yazımını kolaylaştırır
                             <?php
// ===== SERVICE INTERFACE =====

namespace App\Contracts\Internal;

use App\Dtos\BaseDTO;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Model;

/**
* @template TModel of Model
* @template TDTO of BaseDTO
* @template TUpdateDTO of BaseDTO
* @template TListDTO of BaseDTO
*/
interface ServiceInterface
{
/**
* @param TListDTO $data
* @return LengthAwarePaginator<TModel>
*/
public function all(BaseDTO $data, array $with = []): LengthAwarePaginator;

/**
* @param TDTO $data
* @return TModel
*/
public function create(BaseDTO $data): Model;

/**
* @param TModel $model
* @return TModel
*/
public function show(Model $model): Model;

/**
* @param TModel $model
* @param TUpdateDTO $data
* @return TModel
*/
public function update(Model $model, BaseDTO $data): Model;

/**
* @param TModel $model
*/
public function delete(Model $model): bool;
}


// ===== BASE SERVICE =====
<?php

namespace App\Services\Internal;

use App\Contracts\Internal\ServiceInterface;
use App\Dtos\BaseDTO;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Model;

/**
* @template TModel of Model
* @template TDTO of BaseDTO
* @template TUpdateDTO of BaseDTO
* @template TListDTO of BaseDTO
*
* @implements ServiceInterface<TModel, TDTO, TUpdateDTO, TListDTO>
*/
class BaseService implements ServiceInterface
{
/**
* @var TModel
*/
protected $model;

/**
* @param TModel $model
*/
public function __construct(Model $model)
{
$this->model = $model;
}

/**
* @param TListDTO $data
* @return LengthAwarePaginator<TModel>
*/
public function all(BaseDTO $data, array $with = []): LengthAwarePaginator
{
$query = $this->model->query();

$query->with($with);

if ($data->search) {
$query->where('name', 'like', '%' . $data->search . '%');
}

return $query->paginate($data->per_page);
}

/**
* @param TDTO $data
* @return TModel
*/
public function create(BaseDTO $data): Model
{
return $this->model::create($data->toArray());
}

/**
* @param TModel $model
* @return TModel
*/
public function show(Model $model): Model
{
return $this->model->find($model->id);
}

/**
* @param TModel $model
* @param TUpdateDTO $data
* @return TModel
*/
public function update(Model $model, BaseDTO $data): Model
{
$this->model->find($model->id)->update($data->toArray());

return $this->model;
}

/**
* @param TModel $model
*/
public function delete(Model $model): bool
{
return $this->model->find($model->id)->delete();
}
}


// ===== BaseDTO =====
<?php

namespace App\Dtos;

abstract class BaseDTO
{
public int $per_page;
public ?string $search;
abstract public static function fromRequest(array $data): static;
abstract public function toArray(): array;

protected function searchStrReplace(string $search): string
{
$search = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $search);

return $search;
}
}


// ===== MessageServiceInterface =====
<?php

namespace App\Contracts\Internal;

use App\Contracts\Internal\ServiceInterface;

/**
* @extends BaseService<\App\Models\Message, \App\Dtos\Messages\MessageDTO, \App\Dtos\Messages\MessageDTO, \App\Dtos\Messages\MessageListDTO>
*/
interface MessageServiceInterface extends ServiceInterface
{
}


// ===== MessageService =====
<?php

namespace App\Services\Internal;

use App\Contracts\Internal\MessageServiceInterface;
use App\Models\Message;
use App\Services\Internal\BaseService;

/**
* @extends BaseService<\App\Models\Message, \App\Dtos\Messages\MessageDTO, \App\Dtos\Messages\MessageDTO, \App\Dtos\Messages\MessageListDTO>
*/
class MessageService extends BaseService implements MessageServiceInterface
{
/**
* Create a new class instance.
*/
public function __construct(Message $model)
{
parent::__construct($model);
}
}
📌 Yazı Bilgileri
  • Hedef: Controller’lardan iş mantığını ayırarak, tekrar eden CRUD işlemlerini tek merkezden yönetmek
  • Amacı: Laravel projelerinde sürdürülebilir, okunabilir ve genişletilebilir bir servis katmanı oluşturmak
  • Mimari: Generic Service Pattern / Service Layer Mimarisi
  • DTO Kullanımı: Request yerine DTO ile katmanlar arası temiz ve kontrollü veri aktarımı
  • Tip Güvenliği: PHP Generic (Template) yapısı ile model ve DTO tiplerinin netleştirilmesi
  • Kullanım Alanı: Orta ve büyük ölçekli Laravel projeleri, kurumsal API ve backend mimarileri
  • Service Katmanı: Interface + BaseService + Model’e Özel Service yapısı
  • Test Edilebilirlik: Service katmanının izole yapısı sayesinde unit test yazımının kolaylaşması
  • Genişletilebilirlik: Model’e özel servislerde ekstra iş kurallarının kolayca eklenebilmesi
  • Kod Tekrarı Yönetimi: Ortak CRUD işlemlerinin BaseService üzerinden tek seferde tanımlanması
  • Pagination & Listeleme: DTO üzerinden dinamik arama ve sayfalama desteği
  • Bağımlılık Yönetimi: Constructor üzerinden Model injection ile esnek ve test edilebilir yapı
🏷 Etiketler
Laravel PHP Generic Service Service Layer DTO Clean Architecture SOLID Backend Architecture Design Patterns CRUD Dependency Injection