Sorgu iş kurallarını Eloquent local scope'larda mı yoksa repository katmanında mı tutmalıyım?
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.
Cevap
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
- Asıl sorun controller’daki sorgu mantığı ve ikisi de bunu çözer.
wherebloklarını controller’dan çıkarmak kazancın %80’idir. Scope’lar bunu repository katmanından çok daha az tören ile yapar. - 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.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(); - “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.
Ne yapmalı
- 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.
- Eloquent’i 1:1 saran repository yazmayın.
UserRepository::find()içindeUser::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. - 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.
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.