# Sorgu iş kurallarını Eloquent local scope'larda mı yoksa repository katmanında mı tutmalıyım?

> Sorgu kurallarını local scope'larda tutup gerçek bir test veritabanına karşı test edin; repository'yi yalnız domain Eloquent'ten ayrışacaksa ekleyin.

- Soruldu: 2026-10-22
- Yanıtlandı: 2026-10-27
- Soran: Gizem
- Etiketler: Laravel, Eloquent, Mimari
- Kaynak: https://muhammetsafak.com/tr/sor-bakalim/sorgu-is-kurallarini-eloquent-local-scopelarda-repository-katmaninda/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
**Soru:** Laravel ile bir uygulama geliştiriyorum ve sorgu mantığı controller'larıma yayılmaya başladı; aynı `where` bloklarını birden çok yerde tekrar ediyorum ve bu beni rahatsız ediyor.

İki seçenek arasında kaldım: akıcı Eloquent local scope'ları mı kullanmalıyım, yoksa test edilebilirlik uğruna repository katmanı mı eklemeliyim? Repository'lerin sorgu mantığını soyutlayıp mock'lanabilir kıldığını duyuyorum ama bunun bu proje için gereğinden ağır olmasından da korkuyorum.


## Kısa cevap

Çoğu Laravel uygulaması için doğru varsayılan local scope'lardır — test edilebilir, birleştirilebilir ve idiomatic'tirler.

## Bağlam

Repository'ye ancak gerçek bir sebebiniz varsa uzanın (veri kaynağını değiştirmek ya da bir domain sınırını korumak gibi); "test edilebilirlik" için refleksle değil.

## Neden

1. **Asıl sorun controller'daki sorgu mantığı ve ikisi de bunu çözer.** `where` bloklarını controller'dan çıkarmak kazancın %80'idir. Scope'lar bunu repository katmanından çok daha az tören ile yapar.
2. **Local scope: idiomatic, birleştirilebilir, ucuz.** `Order::active()->forCustomer($id)->recent()` domain gibi okunur. Bunları gerçek bir test DB'sinde (in-memory sqlite ya da test transaction'ı) rahatça test edersiniz; sorgunuzu test etmek için Eloquent'i mock'lamanız gerekmez.
   ```php
   class Order extends Model
   {
       public function scopeActive(Builder $query): void
       {
           $query->where('status', 'active');
       }

       public function scopeForCustomer(Builder $query, int $customerId): void
       {
           $query->where('customer_id', $customerId);
       }
   }
   // Kullanım: Order::active()->forCustomer($id)->get();
   ```
3. **"Test için repository" argümanı Laravel'de göründüğünden zayıftır.** Eloquent tasarım gereği bir Active Record'dur, DB'ye sıkı bağlıdır. Bir repository interface'ini mock'lamak, sorgunuzun doğru olduğunu değil, bir metodu çağırdığınızı test eder. Daha değerli test, [gerçek veritabanına giden testtir](https://muhammetsafak.com/tr/sor-bakalim/eloquentte-n-1i-erken-yakalamak-icin-model-preventlazyloadingi-yalnizca-lokalde-mi/).

## Ne yapmalı

1. **Repository ne zaman yerini hak eder?** Arkadaki veri kaynağını gerçekten değiştirmeniz gerektiğinde (Eloquent → dış API / arama index'i), Eloquent modelinden ayrı bir domain modeliyle DDD yapıyorsanız ya da sorgu kurma mantığının domain katmanına sızmasını yasaklamak istediğinizde. Bunlar mimari kararlardır, test kararları değil.
2. **Eloquent'i 1:1 saran repository yazmayın.** `UserRepository::find()` içinde `User::find()` çağırmak bir katman ekler ama dolaylılıktan başka hiçbir şey satın almaz. En sık gördüğüm anti-pattern budur.
3. **Ara yol: sorgu (query) sınıfları / action'lar.** Karmaşık, çok bloklu sorgular için özel bir query sınıfı (ya da Spatie'nin query builder'ı) tam repository soyutlamasına girmeden isimlendirilmiş, test edilebilir bir nesne verir.

**Sonuç:** Ben olsam sorgu mantığını scope'larda tutar ve onları gerçek bir (sqlite/pgsql) test DB'sine karşı test ederdim. Repository'yi yalnızca domain'in Eloquent'ten gerçekten ayrışması gerektiğinde eklerim — o zaman da Eloquent modeli değil, domain nesnesi döndürürler. Sırf mock'lamayı mümkün kılmak için repository eklemek, çoğu zaman gerçek bir testi sahte bir testle takas etmektir.

## İlgili Yazılar

- [Birçok modele bağlanan yorumlar için polymorphic ilişki mi yoksa ayrı tablolar mı; index ve foreign key açısından hangisi daha sağlıklı?](https://muhammetsafak.com/tr/sor-bakalim/bircok-modele-baglanan-yorumlar-polymorphic-iliski-ayri-tablolar/) — Sor Bakalım
- [İşlediğim satırları aynı anda güncellerken chunk yerine chunkById mı kullanmalıyım?](https://muhammetsafak.com/tr/sor-bakalim/isledigim-satirlari-ayni-anda-guncellerken-chunk-yerine-chunkbyid-mi-kullanmaliyim/) — Sor Bakalım
- [Integer cent olarak sakladığım para alanları için custom cast mı yoksa accessor/mutator mı kullanmalıyım?](https://muhammetsafak.com/tr/sor-bakalim/integer-cent-olarak-sakladigim-para-alanlari-icin-custom-cast-mi-yoksa/) — Sor Bakalım
