Laravel Architecture Rehberi: Katmanlı Yapı

# Rehber: Laravel Architecture Laravel, 2011'de Taylor Otwell tarafından yayınlandığından bu yana PHP ekosisteminin en çok tercih edilen framework'le...

Laravel Architecture Rehberi: Katmanlı Yapı

Rehber: Laravel Architecture

Laravel, 2011'de Taylor Otwell tarafından yayınlandığından bu yana PHP ekosisteminin en çok tercih edilen framework'lerinden biri haline geldi. Ancak çoğu geliştirici Laravel'i yalnızca "hızlı prototip üretme aracı" olarak görüyor; oysa framework'ün asıl gücü, sunduğu mimari esneklikte ve katmanlı yapıda yatıyor. Bu rehberde Laravel'in mimari temellerini, katmanlarını ve gerçek dünya projelerinde nasıl doğru kurgulanması gerektiğini ele alacağız.

İster yeni bir projeye başlıyor olun, ister mevcut bir Laravel uygulamasını refactor etmeyi planlıyor olun, bu yazı size hem kavramsal hem de pratik bir yol haritası sunacak.

Laravel'in Mimari Felsefesi

Laravel, "convention over configuration" (yapılandırma yerine gelenek) prensibini benimser. Bu yaklaşım, geliştiricinin onlarca konfigürasyon dosyasıyla boğuşmasını engellerken, framework'ün kendi belirlediği standartlar üzerinden tutarlı bir geliştirme deneyimi sunar.

Framework'ün mimari felsefesini üç ana başlıkta özetleyebiliriz:

  • Service Container (IoC Container): Sınıflar arası bağımlılıkları yöneten merkezi bir yapı.
  • Service Providers: Uygulamanın bootstrap sürecinde servisleri kaydeden ve yapılandıran bileşenler.
  • Facades: Karmaşık sınıflara basit, statik benzeri bir arayüz sağlayan yapılar.

Bu üç kavram, Laravel'in neredeyse tüm mimarisinin temelini oluşturur. Şimdi bunları sırasıyla inceleyelim.

Request Lifecycle: Bir İsteğin Yolculuğu

Laravel'de bir HTTP isteği geldiğinde, arka planda belirli bir sırayla işlenir. Bu akışı anlamak, mimari kararlarınızı doğru vermeniz için kritik önemdedir.

  1. public/index.php: Tüm istekler bu dosyadan girer. Burada Composer autoloader yüklenir ve framework'ün bootstrap süreci başlatılır.
  2. HTTP Kernel: İsteği alır, middleware'lerden geçirir ve Service Provider'ları yükler.
  3. Service Providers: Uygulamanın tüm servisleri (veritabanı, cache, queue vb.) bu aşamada register edilir ve boot edilir.
  4. Router: İsteği ilgili route'a yönlendirir.
  5. Middleware: İstek, controller'a ulaşmadan önce tanımlı middleware'lerden geçer.
  6. Controller / Closure: İsteği işleyen asıl kod çalışır.
  7. Response: Oluşturulan yanıt, tekrar middleware katmanından geçerek kullanıcıya döner.

Bu akışı bilmek, özellikle performans optimizasyonu ve debug süreçlerinde size büyük avantaj sağlar.

Katmanlı Mimari Yaklaşımı

Laravel, kutudan çıktığı haliyle MVC (Model-View-Controller) yapısını sunar. Ancak orta ve büyük ölçekli projelerde MVC tek başına yeterli olmaz. Sağlıklı bir Laravel mimarisi için şu katmanları düşünmelisiniz:

Controller Katmanı

Controller'lar yalnızca isteği alıp uygun servise yönlendirmekle sorumlu olmalıdır. İçinde iş mantığı (business logic) barındırmamalıdır.

class OrderController extends Controller
{
    public function __construct(
        private OrderService $orderService
    ) {}

    public function store(StoreOrderRequest $request)
    {
        $order = $this->orderService->create(
            $request->validated()
        );

        return new OrderResource($order);
    }
}

Yukarıdaki örnekte controller, yalnızca doğrulama ve servis çağrısı yapıyor. İş mantığı OrderService içinde.

Service Katmanı

Service katmanı, iş kurallarının kalbidir. Controller'dan gelen veriyi alır, gerekli işlemleri yapar ve sonucu döner.

class OrderService
{
    public function __construct(
        private OrderRepository $repository,
        private PaymentGateway $payment
    ) {}

    public function create(array $data): Order
    {
        DB::beginTransaction();

        try {
            $order = $this->repository->create($data);
            $this->payment->charge($order);

            DB::commit();
            return $order;
        } catch (\Throwable $e) {
            DB::rollBack();
            throw $e;
        }
    }
}

Repository Katmanı

Veritabanı erişimini soyutlar. Böylece Eloquent'e olan bağımlılığınız azalır ve test edilebilirlik artar.

interface OrderRepository
{
    public function create(array $data): Order;
    public function find(int $id): ?Order;
}

class EloquentOrderRepository implements OrderRepository
{
    public function create(array $data): Order
    {
        return Order::create($data);
    }

    public function find(int $id): ?Order
    {
        return Order::find($id);
    }
}

Bu yapı sayesinde yarın Eloquent yerine başka bir ORM veya API kullanmak isterseniz, sadece implementasyonu değiştirmeniz yeterli olur.

Service Container ve Dependency Injection

Laravel'in en güçlü özelliklerinden biri, otomatik dependency injection yapabilmesidir. Container, bir sınıfın constructor'ında belirtilen bağımlılıkları otomatik olarak çözer.

class ReportGenerator
{
    public function __construct(
        private PdfRenderer $pdf,
        private MailService $mail
    ) {}
}

Bu sınıfı app(ReportGenerator::class) şeklinde çağırdığınızda, Laravel PdfRenderer ve MailService bağımlılıklarını otomatik olarak enjekte eder.

Interface binding yapmak için Service Provider kullanabilirsiniz:

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(
            OrderRepository::class,
            EloquentOrderRepository::class
        );
    }
}

Bu yapı, SOLID prensiplerinden Dependency Inversion'ı doğal olarak uygulamanızı sağlar.

Modüler ve Domain Odaklı Yapılanma

Proje büyüdükçe, tüm controller'ları tek bir klasörde tutmak kaosa yol açar. Bunun yerine domain odaklı (Domain-Driven Design esintili) bir klasör yapısı tercih edilebilir:

app/
├── Domain/
│   ├── Order/
│   │   ├── Models/
│   │   ├── Services/
│   │   ├── Repositories/
│   │   └── Actions/
│   └── User/
│       ├── Models/
│       ├── Services/
│       └── Repositories/
├── Http/
│   ├── Controllers/
│   ├── Requests/
│   └── Resources/
└── Providers/

Bu yapı, ekiplerin belirli domainler üzerinde bağımsız çalışmasını kolaylaştırır ve kodun okunabilirliğini artırır.

Action Sınıfları: Tek Sorumluluk Prensibi

Bazı durumlarda Service katmanı bile fazla genişleyebilir. Bu noktada "Action" sınıfları devreye girer. Her action, tek bir işi yapar.

class CreateOrderAction
{
    public function execute(array $data): Order
    {
        // tek sorumluluk: sipariş oluştur
    }
}

Controller veya Service içinden app(CreateOrderAction::class)->execute($data) şeklinde çağrılır. Bu yaklaşım özellikle test yazmayı son derece kolaylaştırır.

Event-Driven Mimari

Laravel'in event sistemi, uygulamanızı gevşek bağlı (loosely coupled) hale getirmenin en etkili yollarından biridir. Bir sipariş oluşturulduğunda e-posta göndermek, stok güncellemek, log atmak gibi işlemleri event'lerle ayırabilirsiniz.

event(new OrderCreated($order));

Ardından bir listener:

class SendOrderConfirmation
{
    public function handle(OrderCreated $event): void
    {
        Mail::to($event->order->user)->send(
            new OrderConfirmationMail($event->order)
        );
    }
}

Bu yapı, ana iş akışını bozmadan yan görevler eklemenizi sağlar.

Test Edilebilirlik ve Mimari İlişkisi

İyi bir mimarinin en somut göstergesi, test yazmanın kolay olmasıdır. Katmanlı yapı sayesinde:

  • Service'leri mock'layarak controller testleri yazabilirsiniz.
  • Repository'leri in-memory implementasyonlarla değiştirebilirsiniz.
  • Action sınıflarını izole şekilde test edebilirsiniz.
public function test_order_can_be_created()
{
    $repository = Mockery::mock(OrderRepository::class);
    $repository->shouldReceive('create')->once()->andReturn(new Order());

    $service = new OrderService($repository, app(PaymentGateway::class));
    $order = $service->create(['total' => 100]);

    $this->assertInstanceOf(Order::class, $order);
}

Yaygın Mimari Hatalar

Deneyimlerime göre Laravel projelerinde en sık yapılan mimari hatalar şunlardır:

  • Fat Controller: İş mantığını controller içine yazmak.
  • Fat Model: Eloquent model'lerine onlarca ilişki ve iş kuralı yığmak.
  • Facade Bağımlılığı: Her yerde DB::, Cache:: gibi facade'ları doğrudan kullanmak (test edilebilirliği düşürür).
  • Service Provider Şişkinliği: Tüm binding'leri tek provider'a doldurmak.
  • Event Suistimali: Basit işlemler için bile event kullanmak, akışı takip edilemez hale getirir.

Bu hataların çoğu, katmanları doğru ayırmamaktan kaynaklanır.

Sonuç

Laravel, sunduğu esnek mimari sayesinde hem küçük projelerde hem de kurumsal ölçekli uygulamalarda rahatça kullanılabilir. Ancak framework'ün sağladığı kolaylıklar, beraberinde disiplin gerektirir. Controller'ı ince tutmak, iş mantığını Service katmanına taşımak, veri erişimini Repository ile soyutlamak ve event-driven yaklaşımı ölçülü kullanmak; uzun vadede bakımı kolay, test edilebilir ve ölçeklenebilir bir kod tabanı oluşturmanın anahtarıdır.

Mimari bir defada oturmaz; proje büyüdükçe evrilir. Önemli olan, doğru prensipleri baştan benimseyip gerektiğinde cesurca refactor edebilmektir. Laravel bu yolculukta size hem esneklik hem de sağlam bir temel sunar.

İLGİLİ İÇERİKLER

Bunları da Okumak İsteyebilirsiniz