Repository Pattern Nedir, PHP Projelerinde Nasıl Kullanılır?

Google News Google News Flipboard Flipboard Sesli oku Yazıyı beğen Favorilere Ekle 0 Yorumlar
Daha fazla

repository pattern, veri erişim katmanını iş mantığından ayırarak test edilebilirliği, sürdürülebilirliği ve teknoloji değişim esnekliğini artıran klasik bir mimari kalıptır. PHP projelerinde özellikle Laravel, Symfony gibi framework’lerin ORM’leriyle çalışırken doğru uygulandığında büyük kazanç sağlar. Bu rehber, sıfırdan uygulama, test stratejileri ve gerçek dünya senaryoları üzerinden ilerler.

repository pattern nedir ve neden önemlidir?

repository pattern nedir ve neden önemlidir?
repository pattern nedir ve neden önemlidir?

repository pattern, Eric Evans’ın Domain-Driven Design kitabında tanımladığı temel yapı taşlarından biridir. Amaç, veri kaynağını (veritabanı, API, dosya) bir koleksiyon gibi ele alan bir soyutlama katmanı sunmaktır. Üst katman (Service, Controller) “veriyi nasıl alacağını” bilmez; sadece “şu entity’yi getir” der. Bu, üst katmanın ORM’e, SQL’e veya HTTP client’a bağımlılığını kırar.

PHP dünyasında Repository Pattern’in yaygınlaşmasının sebebi, Laravel’in Eloquent ORM’sinin aktif record deseni kullanmasıdır. Aktif record’da model hem veri hem davranış taşır; bu küçük projelerde hız kazandırır ancak büyüdükçe test edilmesi zorlaşır. Repository, veri erişim kodunu dışarı çıkararak bu sorunu çözer.

repository pattern hangi durumlarda kullanılır?

En sık kullanım senaryoları: (a) Birden fazla veri kaynağını (MySQL + Elasticsearch + Redis) tek bir arayüzle sarmak, (b) Karmaşık sorguları tek bir yerde toplamak, (c) Unit testlerde veritabanını mock’lamak, (d) Birden fazla ORM (Doctrine + Eloquent) birlikte kullanmak, (e) Microservice geçişinde veri kaynağını değiştirmek (MySQL → PostgreSQL).

repository pattern nasıl yapılır?

repository pattern nasıl yapılır?
repository pattern nasıl yapılır?

repository pattern için temel kavramlar

Repository’nin kalbinde interface, implementation ve entity kavramları vardır. Interface, dış dünyaya sunulan sözleşmedir: find($id), findByEmail($email), save(User $user), delete($id). Implementation, interface’in somut karşılığıdır; veritabanına erişir, sorguları çalıştırır. Entity, domain’in veri modelidir; genellikle sade bir PHP sınıfıdır (Eloquent’ten bağımsız). Service katmanı sadece interface’i bilir; implementation’ı container’dan alır (Laravel’de service provider, Symfony’de services.yaml).

repository pattern adım adım uygulama rehberi

Adım adım uygulama:

  1. Entity tanımı: Sadece property + getter/setter (veya immutable + with-pattern). Framework annotation’larına bağımlı değil.
  2. Repository interface: Domain’in ihtiyaç duyduğu sorguları yansıtır. “Generic CRUD + özel sorgular” yaklaşımı yaygındır.
  3. EloquentRepository implementasyonu: Interface’in Eloquent’e özel karşılığı. Burada Eloquent query builder veya model sınıfları kullanılır.
  4. Service katmanı entegrasyonu: Service, constructor injection ile repository interface’ini alır. Asla somut sınıfı bilmez.
  5. Service provider kaydı: Laravel’de $this->app->bind(UserRepository::class, EloquentUserRepository::class); veya kontrat binding.
  6. Test stratejisi: Unit testlerde mock repository; integration testlerde gerçek implementasyon SQLite veya test database ile.

Örnek minimal interface:

interface UserRepository {
    public function find(int $id): ?User;
    public function findByEmail(string $email): ?User;
    public function save(User $user): void;
    public function delete(int $id): void;
}

repository pattern ayarları ve dikkat edilmesi gerekenler

repository pattern ayarları ve dikkat edilmesi gerekenler
repository pattern ayarları ve dikkat edilmesi gerekenler

repository pattern için gerekli araçlar ve ön hazırlık

Yaygın paketler: Prettus L5-Repository (Laravel için en popüler, filtre + criteria + cache desteği), BNOTATIONS/laravel-repository (daha sade), Doctrine ORM ile native repository pattern, Cycle ORM (Doctrine benzeri ama PHP’ye özel). Hepsinin temel felsefesi aynıdır: sözleşmeyi ayır, bağımlılığı tersine çevir. Paket seçimi projenin büyüklüğüne ve ekibin alışkanlığına bağlıdır.

repository pattern yaparken sık yapılan hatalar

Yapılan hatalar: (1) Repository içine iş mantığı koymak (fiyat hesaplama, validation), (2) “Her model için repository” yazıp CRUD’a indirgemek, (3) Lazy loading’i unutup N+1 sorgu problemi yaratmak, (4) Generic repository ile tüm projeyi tek interface’e sığdırmaya çalışmak, (5) Interface’i hiç kullanmadan sadece somut sınıf yazmak. Bu hataların en tehlikelisi birincisidir çünkü repository’nin avantajı “veri erişimi” olmasıdır; iş mantığı service’te kalmalıdır.

repository pattern performans, güvenlik ve SEO etkileri

Performans: Repository, Eloquent üzerine kurulduğunda ek yükü düşüktür (sadece bir method çağrısı fazlası). Ancak sorgu optimizasyonu repository’nin sorumluluğundadır; eager loading, query caching, chunking gibi teknikler burada uygulanmalıdır. Güvenlik: SQL injection, repository’nin sağladığı parametreli sorgular ile doğal olarak engellenir; ancak “raw” query kullanımı repository’nin sınırını ihlal eder. Test edilebilirlik: Repository, unit testlerde tamamen mock’lanabilir; integration testlerde ise gerçek veritabanı ile çalıştırılır. Bu hibrit yaklaşım, hızlı test + güvenilir doğrulama sağlar.

repository pattern kontrol listesi ve sık sorulan sorular

repository pattern yayın öncesi son kontrol listesi

Yayın öncesi kontrol listesi:

  • Repository interface’leri domain’e özgü mü, generic CRUD değil mi?
  • Service sadece interface’e mi bağımlı?
  • Repository içinde iş mantığı var mı?
  • N+1 sorgu kontrolü yapıldı mı?
  • Unit testler mock repository ile mi çalışıyor?
  • Integration testler gerçek DB ile mi?
  • Binding (DI) doğru mu?

Sıkça Sorulan Sorular

Repository Pattern her projede gerekli mi?
Hayır. Küçük, tek geliştiricili, kısa ömürlü projelerde overhead yaratır. Ekip büyüklüğü 2+ olan, 1 yıldan uzun yaşayacak, birden fazla veri kaynağı olabilecek projelerde kesinlikle değer katar.

Laravel Repository Pattern, Eloquent'i tamamen gizler mi?
Tamamen gizlemez; implementasyon hâlâ Eloquent kullanır. Ancak Service katmanı Eloquent'i bilmez, sadece interface'i bilir. Bu sayede Eloquent'ten vazgeçmek istediğinizde sadece implementasyonu değiştirirsiniz.

Repository ile Active Record (Eloquent) arasında nasıl seçim yapılır?
Proje büyüklüğüne, ekip olgunluğuna ve test ihtiyacına göre. Hızlı prototip veya küçük uygulamada Eloquent yeterli; kurumsal, uzun ömürlü, test-yoğun projelerde Repository önerilir. İkisini birlikte kullanmak da mümkündür: Service sadece Repository'i bilir, Repository Eloquent'i sarmalar.

Repository içinde cache'leme yapılmalı mı?
Genel kanı "hayır"dır. Cache'leme service veya decorator katmanında yapılır; repository sadece saf veri erişiminden sorumlu olur. Bu separation of concerns prensibine uygundur.

Doctrine ORM, Repository Pattern için daha mı uygun?
Doctrine, Repository Pattern'i native destekler (EntityRepository sınıfı). Laravel/Eloquent'te ise elle kurmanız gerekir. Doctrine ile çalışıyorsanız hazır yapıyı kullanın; Laravel'de ise Prettus L5-Repository gibi paketler devreye girer.

Repository'de transaction yönetimi nasıl yapılır?
Transaction yönetimi repository'de değil service katmanında yapılmalıdır. Birden fazla repository'yi kapsayan transaction (ör. sipariş oluşturma + stok düşme + fatura kaydı) service'in sorumluluğundadır. Service, DB::transaction veya UnitOfWork pattern'i ile süreci yönetir; repository sadece tek bir sorgudan sorumludur.

Repository performansı ölçmek için hangi metriklere bakılmalı?
En önemli metrikler: sorgu süresi (P50, P95, P99), N+1 sorgu sayısı, cache hit oranı, eager loading kullanımı, toplam query count (request başına). Laravel Telescope, Doctrine SQL Logger veya DataDog APM ile ölçülür. Hedef: P95 < 50ms, request başına sorgu sayısı < 20.

Repository'de pagination nasıl ele alınmalı?
Repository, pagination'ı soyutlamalıdır. Interface'te <code>paginate(int $page, int $perPage, array $filters = [])</code> benzeri bir metot sunulur; implementation (Eloquent/Doctrine) kendi pagination mekanizmasını kullanır. Sayfalama sonucu hem datayı hem toplam kayıt sayısını döner; client tarafında total_pages hesaplanır.

Repository'de caching nasıl uygulanır?
Cache'leme repository'nin içinde değil, service veya dekoratör (decorator) katmanında uygulanmalıdır. Örnek: <code>CachedUserRepository</code>, orijinal <code>UserRepository</code>'yi sarmalar; find() çağrısında önce Redis'e bakar, yoksa orijinal repository'yi çağırır. Bu sayede test sırasında cache'i kapatmak için dekoratörü devre dışı bırakmak yeterlidir.

Gerçek Dünya Senaryoları

Bir Laravel e-ticaret projesi düşünün: başlangıçta doğrudan Eloquent kullanılıyordu, controller'lar doğrudan model'e bağlıydı. 18 ay sonra test yazılmaya başlandığında her test gerçek veritabanı gerektiriyordu, CI pipeline 45 dakikaya çıktı. Repository Pattern'i uyguladıktan sonra controller'lar repository interface'ine bağlandı, service'ler mock'landı, unit testler 2 saniyede bitti. CI süresi 45 dakikadan 7 dakikaya düştü.

Bir SaaS ürününde raporlama ihtiyacı doğdu: standart Eloquent sorguları yavaştı, özel SQL yazılması gerekiyordu. Repository katmanı sayesinde raporlama sorguları tek bir yerde toplandı, performans optimizasyonu (index'ler, materialized view) kolaylaştı. Controller ve service kodu değişmedi.

Bir entegrasyon projesi: aynı domain için hem MySQL hem de API üzerinden veri çekmek gerekiyordu (master + slave mimarisi). İki ayrı repository implementasyonu yazıldı (MysqlOrderRepository, ApiOrderRepository), service katmanı değişmedi. Aynı service, iki farklı veri kaynağıyla çalışabildi.

Yaygın Tuzaklar ve Çözümleri

Tuzak 1: Repository içine iş mantığı koymak. Repository "findActiveUsers" dışında bir şey döndürmemeli; filtreleme + sıralama OK ama hesaplama/fiyat/validation YAPILMAMALI. Bu, service'in görevidir. Çözüm: code review'da "bu method service'te mi olmalıydı?" sorusu.

Tuzak 2: Generic CRUD repository ile her şeyi sığdırmak. User, Order, Product için ortak bir "BaseRepository" yazmak cazip ama domain'e özel sorgular (findByEmail, findActiveInLast30Days) için yetersiz kalıyor. Çözüm: her entity için ayrı interface, sadece gerçekten paylaşılan method'lar base'de.

Tuzak 3: N+1 sorgu. Repository, eager loading'i unutuyor, her iterasyonda lazy load tetikleniyor. 100 kayıt için 101 sorgu. Çözüm: profil testleri, "N+1 detector" paketleri (Laravel'de Telescope, Doctrine'de SQL logger).

Tuzak 4: Repository'yi abstract etmeyi abartmak. Sadece tek bir ORM (Eloquent) kullanılıyorsa interface + implementation ayrımı fazla olabilir. Çözüm: eğer 2+ implementasyon olasılığı yoksa doğrudan sınıf kullanın; ama test edilebilirlik için interface tutun.

Tuzak 5: Eloquent model'i entity olarak kullanmak. Eloquent model hem veri hem ilişki hem event taşır; service'e Eloquent model dönmek service'i Eloquent'e bağlar. Çözüm: DTO veya sade PHP entity dönmek, dönüşüm repository içinde.

Gelecek Trendleri

Trend 1: "Repository as a Service". Büyük ölçekli sistemlerde her domain için ayrı microservice olarak repository; sorgu CQRS (Command Query Responsibility Segregation) pattern'i ile yapılıyor. Okuma için optimize edilmiş ayrı bir model, yazma için optimize edilmiş ayrı bir model.

Trend 2: Otomatik query optimizasyonu. AI destekli araçlar repository sorgularını analiz edip index önerisi, sorgu yeniden yazımı, materialized view önerisi üretiyor. Hâlâ deneysel ama potansiyeli yüksek.

Trend 3: GraphQL repository. REST + JSON yerine GraphQL ile tek endpoint'ten esnek sorgu. Repository, GraphQL schema'sını implement eder; istemci ihtiyacı kadar alanı çeker.

Trend 4: Multi-tenancy native. Repository, tenant_id otomatik ekleyerek her sorguyu tenant-scoped yapar. Yanlışlıkla başka tenant verisi sızması engellenir. SaaS için güçlü bir trend.

Veri Erişim Desenlerinin Karşılaştırması

Özellik Aktif Record Repository Pattern Data Mapper
Öğrenme Eğrisi Düşük Orta Yüksek
Test Edilebilirlik Düşük Yüksek Yüksek
Performans Yüksek Yüksek Orta
Kod Tekrarı Orta Düşük Düşük
Domain Saflığı Düşük Yüksek Çok Yüksek
ORM Bağımsızlığı Yok Var Var

Sonuç ve Öneriler

repository pattern, PHP projelerinde test edilebilirliği artırmanın, veri kaynağı esnekliği sağlamanın ve ekip ölçeklenebilirliğini korumanın en temel yollarından biridir. Laravel ile çalışıyorsanız, başlangıçta sadece en kritik entity’ler (User, Order, Product) için repository yazın; geri kalanı Eloquent’te kalsın. Zaman ihtiyaca göre genişletin. Önemli olan başlangıçta interface’i doğru tanımlamak; implementation sonradan değişebilir ama sözleşme kalıcı olmalıdır.

İlgili Makaleler

Daha fazla bilgi için resmi kaynakları inceleyebilirsiniz: OWASP.

Yazar Hakkında

Benzer Yazılar

Bir Cevap Yaz

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir.

0/30 karakter