ana sayfa / 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