home / blog / Laravel'de Alan Odaklı Tasarım: CRUD'un...
Laravel Jan 18, 2026

Laravel'de Alan Odaklı Tasarım: CRUD'un Ötesinde

Temel CRUD'un ötesine geçin ve Laravel uygulamalarınızı Alan Odaklı Tasarım ilkeleriyle yapılandırın — agregalar, değer nesneleri, depolar ve alan olayları.

Laravel'de Alan Odaklı Tasarım: CRUD'un Ötesinde

Varsayılan Laravel Yapısının Sorunu

Laravel'in varsayılan yapısı küçük ve orta ölçekli projeler için mükemmeldir. Ancak uygulamanız büyüdükçe, app/Models, app/Http/Controllers ve app/Services klasörleri birbiriyle ilgisiz kodların toplandığı yerlere dönüşür. Bir User modeli aynı anda kimlik doğrulama, faturalandırma, bildirim tercihleri ve takım yönetimini üstlenebilir.

Domain-Driven Design (DDD), kodu teknik katmanlar yerine iş kavramları etrafında düzenlememiz için bir terminoloji ve desen seti sunar.

Yeniden Yapılandırma: Katmanlardan Alan Adlarına

Teknik kaygılara göre değil, iş alanına göre düzenleyin:

app/
├── Domain/
│   ├── Billing/
│   │   ├── Models/
│   │   │   ├── Invoice.php
│   │   │   └── Subscription.php
│   │   ├── Actions/
│   │   │   ├── CreateInvoice.php
│   │   │   └── ProcessPayment.php
│   │   ├── Events/
│   │   │   └── PaymentReceived.php
│   │   ├── ValueObjects/
│   │   │   └── Money.php
│   │   └── Exceptions/
│   │       └── PaymentFailedException.php
│   ├── Identity/
│   │   ├── Models/
│   │   │   ├── User.php
│   │   │   └── Team.php
│   │   └── Actions/
│   │       ├── RegisterUser.php
│   │       └── InviteTeamMember.php
│   └── Catalog/
│       ├── Models/
│       └── Actions/
├── App/
│   ├── Http/Controllers/    ← thin controllers
│   ├── Console/Commands/
│   └── Providers/

Value Object'ler: İş Kurallarını Kapsülleme

Value object, kimliği olmayan ve tamamen nitelikleriyle tanımlanan değişmez (immutable) bir nesnedir. Money, Email, Address klasik örneklerdir.

class Money
{
    public function __construct(
        private readonly int $amount,
        private readonly string $currency = 'USD'
    ) {
        if ($amount < 0) {
            throw new InvalidArgumentException('Amount cannot be negative');
        }
    }

    public function add(Money $other): self
    {
        if ($this->currency !== $other->currency) {
            throw new CurrencyMismatchException();
        }

        return new self($this->amount + $other->amount, $this->currency);
    }

    public function multiply(int $factor): self
    {
        return new self($this->amount * $factor, $this->currency);
    }

    public function formatted(): string
    {
        return number_format($this->amount / 100, 2) . ' ' . $this->currency;
    }

    public function equals(Money $other): bool
    {
        return $this->amount === $other->amount
            && $this->currency === $other->currency;
    }
}

Artık ham tam sayıları oradan oraya geçirip herkesin "bu cent mi yoksa dolar mı?" diye hatırlamasını ummak yerine, kendini belgeleyen ve kendini doğrulayan bir türünüz var.

Action'lar: Tek Amaçlı İş Operasyonları

Action'lar (ayrıca "use case" veya "command" olarak da bilinir) tek bir iş operasyonunu kapsüller. Alan mantığınıza giriş noktasıdırlar.

class CreateInvoice
{
    public function __construct(
        private readonly InvoiceRepository $invoices,
        private readonly TaxCalculator $taxCalculator,
    ) {}

    public function execute(CreateInvoiceData $data): Invoice
    {
        $subtotal = $data->lineItems->sum(
            fn (LineItem $item) => $item->total()->amount
        );

        $tax = $this->taxCalculator->calculate(
            new Money($subtotal),
            $data->taxRegion
        );

        $invoice = $this->invoices->create([
            'customer_id' => $data->customerId,
            'subtotal' => $subtotal,
            'tax' => $tax->amount,
            'total' => $subtotal + $tax->amount,
            'due_date' => $data->dueDate,
            'status' => InvoiceStatus::Draft,
        ]);

        event(new InvoiceCreated($invoice));

        return $invoice;
    }
}

Domain Event'ler: Yan Etkileri Ayrıştırma

Alan mantığınızda önemli bir şey olduğunda, bir event tetikleyin. Bu, çekirdek mantığınızı temiz tutar ve sistemin diğer bölümlerinin bağımsız olarak tepki vermesini sağlar.

// The event — a simple data carrier
class PaymentReceived
{
    public function __construct(
        public readonly int $invoiceId,
        public readonly Money $amount,
        public readonly string $paymentMethod,
        public readonly Carbon $paidAt,
    ) {}
}

// Listeners handle side effects
class SendPaymentConfirmation
{
    public function handle(PaymentReceived $event): void
    {
        $invoice = Invoice::findOrFail($event->invoiceId);

        Mail::to($invoice->customer->email)->send(
            new PaymentConfirmationMail($invoice, $event->amount)
        );
    }
}

class UpdateAccountBalance
{
    public function handle(PaymentReceived $event): void
    {
        // Update the customer's balance
    }
}

class NotifyAccountingTeam
{
    public function handle(PaymentReceived $event): void
    {
        // Send Slack notification
    }
}

Data Transfer Object'ler

DTO'lar, davranış içermeden katmanlar arasında veri taşır. Fonksiyon imzalarınızı açık ve yeniden düzenlemeye uygun hale getirir.

class CreateInvoiceData
{
    public function __construct(
        public readonly int $customerId,
        public readonly Collection $lineItems,
        public readonly string $taxRegion,
        public readonly Carbon $dueDate,
    ) {}

    public static function fromRequest(Request $request): self
    {
        return new self(
            customerId: $request->integer('customer_id'),
            lineItems: collect($request->input('line_items'))->map(
                fn ($item) => new LineItem($item['description'], $item['quantity'], $item['unit_price'])
            ),
            taxRegion: $request->string('tax_region'),
            dueDate: Carbon::parse($request->input('due_date')),
        );
    }
}

DDD Ne Zaman Uygulanmalı (Ve Ne Zaman Uygulanmamalı)

  • DDD kullanın: İş mantığınız karmaşık olduğunda, birden fazla kural içerdiğinde ve sık değiştiğinde
  • DDD'yi atlayın: Basit CRUD uygulamaları, prototipler veya yönetim panelleri için
  • Basit başlayın -- karmaşıklık arttıkça her zaman DDD'ye doğru yeniden yapılandırabilirsiniz
  • Her DDD desenini uygulamayın -- gerçek sorunlarınızı çözen desenleri seçin

"DDD'nin amacı kitaptaki her deseni kullanmak değildir. Amaç, geliştiriciler ve alan uzmanları arasında alan hakkında ortak bir anlayış oluşturmak ve bu anlayışı kodda yansıtmaktır."

tum yazilara don