Sorgu Probleminiz Var. Henüz Kabul Etmediniz.
Sorgu Probleminiz Var. Henüz Kabul Etmediniz.
Normal bir Laravel kod tabanını tanımlayalım.
- Bir controller aktif kullanıcıları alır
- Bir işlem (job) neredeyse aynı kullanıcıları, bir ek koşul ile alır
- Bir servis tekrar alır, bir koşulu atlar
- Bir test sorguyu baştan inşa eder
Aynı amaç. Farklı sorgular. Farklı sonuçlar.
Hiçbir zaman fark edilmez, bir şeyler bozulana kadar. Sonra herkes hangi versiyonun “doğru” olduğunu tahmin etmeye başlar.
İşte problem bu.
Sorun karmaşıklıkta değil. Ölçekle de değil.
Tutarsızlıkta.
Problem Kopyalama Değil. Dalgınlık (Drift).
Problem Kopyalama Değil. Dalgınlık (Drift).
İnsanlar “kendi kendini tekrarlama” demeyi sever.
Ama hâlâ her yerde bunu yazıyorlar:
User::where('active', true)->get();
Sonra başka bir yerde:
User::where('active', true)
->whereNotNull('email_verified_at')
->get();
Sonra başka bir yerde:
User::where('active', true)
->where('role', 'admin')
->get();
Artık kopyalama yok.
Fakat aynı şeyin farklı tanımları var.
Bu daha kötüsü.
Çünkü şimdi sisteminiz hangi dosyanın açıldığına bağlı olarak farklı davranıyor.
Kural: Her Sorguyu Bir Katman Yönetir
Kural: Her Sorguyu Bir Katman Yönetir
Bu durumu durdurmanın tek yolu var:
Bütün veritabanı sorguları tek bir katmanda yer almalıdır. O katman AQC’dir.
Ve bir kez var olduğunda:
- Kontrolörler sorgu yazmaz
- Servisler sorgu yazmaz
- İşlemler sorgu yazmaz
- Testler sorgu yazmaz
Eğer bir sorgu AQC dışındaysa, yanlış. Tartışmasız. AQC ihlalidir.
Bir kontrolörde şöyle olmaktansa:
public function index(Request $request)
{
$users = User::where('active', true)
->whereNotNull('email_verified_at')
->get();
return view('users.index',compact('users'));
}
Bunu yapmalısınız:
public function index(Request $request)
{
$users = (new GetUsers())->handle(['active' => true]);
return view('users.index',compact('users'));
}
Artık sorgu tek bir ev sahibi var.
Aynı Kural Her Yerde
Aynı Kural Her Yerde
Kontrolör
Kontrolör
$users = (new GetUsers())->handle([
'role' => $request->role,
]);
Servis
Servis
$users = (new GetUsers())->handle([
'digest_enabled' => true,
]);
İşlem
İşlem
$users = (new GetUsers())->handle([
'active' => false,
]);
Test
Test
$users = (new GetUsers())->handle([
'active' => true,
]);
Artık kimse sorgu yazmıyor.
Sadece kullanıyorlar.
Peki ya ne oldu? Kompozisyon elde ettiniz.
AQC ile tek bir sorgu sınıfı, kendisini tekrar etmeden birden fazla bağlamı ele alabilir.
Aşağıdaki örneğe bakın:
namespace App\AQC\User;
use App\Models\User;
class GetUsers
{
public function handle(array $params = [])
{
$query = User::query();
// koşulları koşullu olarak uygula
if (!empty($params['active'])) {
$query->where('active', $params['active']);
}
if (!empty($params['digest_enabled'])) {
$query->where('digest_enabled', $params['digest_enabled']);
}
if (!empty($params['role'])) {
$query->where('role', $params[]);
}
// Sıralama uygulandığında varsayılan olarak id'ye göre sıralar
if(isset($params[]) && isset($params[])){
$sortBy = $params[];
$type = $params[];
$query->orderBy(, );
}
return isset([])
? ->paginate(User::PAGINATE)
: ->get();
}
}
Burada olan şey şu:
- Koşullu filtreler – Aktif kullanıcılar, inceleme (digest)-aktif kullanıcılar, rol tabanlı filtreleme – hepsi arayıcı tarafından kontrol ediliyor. Hiçbir şey birden fazla yerde sert kodlanmamış.
- Esnek sıralama – Sorgu dinamik sıralamayı destekliyor, ancak hiçbir şey belirtilmezse öngörülebilir bir davranış gösteriyor.
- Sayfalama veya tam koleksiyon – Tek bir sınıf, UI için sayfalı listeleri ve işler veya dışa aktarmalar için tam koleksiyonları ele alıyor.
Bu, kompozisyonun uygulanmasıdır. Tek bir sorgu sınıfı, her biri kendi parametrelerini geçerek birden fazla tüketiciye hizmet eder. Temel koşullar ve mantık tek bir yerde yaşamaktadır. Hiçbir kontrolör, hizmet veya işlem bir filtreyi yeniden uygulamaz ya da bir koşulu unutur.
Güzel olan? Her tüketici tam olarak ihtiyacı olanı alır ve sorgu mantığını çoğaltmaz veya ayrılmaz.
Kullanıcıları almak için tek bir bilgi kaynağına sahip olursunuz.
Ne Kazanırsınız (Ve Ne Kaybetmeyi Durdurursunuz)
Ne Kazanırsınız (Ve Ne Kaybetmeyi Durdurursunuz)
Sorgular tek bir yerde yaşadığında:
- İş kurallarını yeniden tanımlamayı durdurursunuz
- Koşulları unutmayı durdurursunuz
- Dosyalar arasında hataları takip etmeyi durdurursunuz
- Hangi sorgunun doğru olduğu hakkında tahmin yapmayı durdurursunuz
Değişim bir defa olur. Davranış her yerde güncellenir.
İşte bu kadar.
İhlal Olarak Ne Sayılır
İhlal Olarak Ne Sayılır
Bu pek rahat hissettirmeyecek:
Bu sorguyu AQC dışında herhangi bir yerde yazarsanız:
User::where(...)
Mimaride kırılma yaşadınız. AQC tasarım kurallarını ihlal ettiniz.
“Sadece bir koşul” olmasının, “geçici olmasının” ya da “bu şekilde daha hızlı olduğunun” önemi yoktur.
Yanlış.
AQC yalnızca uygulandığında çalışır. Önerilmez. Ama zorunludur.
Alışkanlık
Alışkanlık
Herhangi bir sorgu yazmadan önce, sadece iki seçeneğiniz var:
- Zaten mevcut → kullanın
- Yok → AQC içinde oluşturun
Üçüncü bir seçenek yoktur.
Son Düşünceler
Son Düşünceler
Atomic Query Construction her sorguyu tek bir ev sahibi vererek tekrar etmeyi ortadan kaldırır. Bir katman. Her eylem için bir sınıf. Bir genel yöntem. Bir parametre imzası. Her tüketici her zaman aynı sorguyu kullanır.
Kontrolörler AQC sınıflarını çağırır. Servisler AQC sınıflarını çağırır. İşlemler AQC sınıflarını çağırır. Testler AQC sınıflarını çağırır. Veritabanı yalnızca bir katmana yanıt verir — ve yalnızca bir katmana.
Tahminleri bırakın. Kopyalamayı bırakın. Dosyalar arasında ince farkları takip etmeyi bırakın. Katmanı oluşturun, kuralları zorlayın ve sorgu disiplinini bir alışkanlık haline getirin.
Bu isteğe bağlı değil. Bu, sürdürülebilir ve öngörülebilir bir Laravel kod tabanının temelidir.
Kaynak: Orijinal Makale


