Repository Pattern
Repository Pattern, veri erişim mantığını business logicndan ayırmak için kullanılan bir yazılım tasarım modelidir. Uygulamanın geri kalanı doğrudan database sorguları yerine net yöntemlerle çalışırken, verileri almak, depolamak, güncellemek ve silmekten sorumlu özel bir katman oluşturur.
Bu model, nesne yönelimli uygulamalarda, backend sistemlerinde, APIs, Laravel projelerinde, Symfony uygulamalarında ve kurumsal yazılımlarda yaygın olarak kullanılır. Geliştiricilerin database işlemlerini düzenlemesine, tekrarları azaltmasına, test edilebilirliği geliştirmesine ve iş kodunu temiz tutmasına yardımcı olur.
Giriş
Birçok uygulamada veriler database'da depolanır. Geliştiricilerin kullanıcı oluşturması, ürünleri bulması, siparişleri güncellemesi, kayıtları silmesi, sonuçları filtrelemesi, listeleri sayfalandırması ve karmaşık sorgular gerçekleştirmesi gerekir. Tüm bu mantık doğrudan denetleyicilerin veya hizmetlerin içine yazılırsa projenin sürdürülmesi zorlaşır.
Repository Pattern, veri erişim işlemlerini depo sınıflarının içine yerleştirerek bu sorunu çözer. Repository, uygulama mantığı ile veri kaynağı arasında orta katman görevi görür.
Uygulama, database sorgularını her yere yazmak yerine findById, findByEmail, getActiveUsers, kaydetme, güncelleme veya silme gibi yöntemleri çağırır. Repository, verilerin gerçekte nasıl alındığını veya saklandığını yönetir.
Repository Pattern Nedir?
Repository Pattern, veri erişimi üzerinde soyutlama sağlayan bir tasarım modelidir. Verilerin nasıl depolandığı ve alındığına ilişkin ayrıntıları gizler ve uygulamanın geri kalanının kullanabileceği basit yöntemleri ortaya çıkarır.
Basit bir ifadeyle depo, database veya veri kaynağıyla konuşmaktan sorumlu bir sınıftır. İş mantığının, verilerin MySQL, PostgreSQL, MongoDB, API, önbellek sistemi veya bir dosyadan gelip gelmediğini bilmesine gerek yoktur.
Örneğin bir UserRepository, findByEmail ve create gibi yöntemler sağlayabilir. Kimlik doğrulama hizmeti, tam SQL sorgusunu veya ORM ayrıntılarını bilmeden bu yöntemleri kullanabilir.
Repository Pattern Neden Önemlidir?
Repository Pattern önemlidir çünkü endişelerin ayrılmasını sağlar. Denetleyiciler database sorguları içermemelidir. İş hizmetleri ham SQL veya ORM'ye özgü ayrıntılarla dolu olmamalıdır. Veri erişimi özel bir katmanda düzenlenmelidir.
database mantığı uygulamaya dağıldığında, veri yapısında veya sorgu kurallarında herhangi bir değişiklik yapılması zorlaşır. Geliştiricilerin birçok dosyayı güncellemesi gerekebilir ve hataların ortaya çıkması kolaylaşır.
Repositorylarla veri erişimi merkezileştirilir. Bu, kodun bakımını, test edilmesini ve yeniden düzenlenmesini kolaylaştırır.
Repository Pattern Olmadan Sorun
Kullanıcı kaydını yöneten ve doğrudan database sorgularını veya ORM çağrılarını kullanan bir denetleyici düşünün:
class RegisterController
{
public function register(Request $request)
{
$existingUser = User::where('email', $request->email)->first();
if ($existingUser) {
throw new RuntimeException('Email already exists.');
}
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => password_hash($request->password, PASSWORD_BCRYPT),
]);
return $user;
}
}Bu, küçük bir uygulamada işe yarayabilir ancak denetleyici artık veri erişimi hakkında çok fazla şey biliyor. Kullanıcı arama mantığı değişirse veya uygulamanın aktif durum veya kiracı kimliği gibi koşullar eklemesi gerekirse, birçok dosyadaki benzer sorguların güncellenmesi gerekebilir.
Repository Pattern bu sorumluluğu bir Kullanıcı Repositorysuna taşır.
PHP'da Temel Repository Pattern Örneği
Bir arayüz ve somut bir uygulama ile basit bir depo oluşturulabilir.
interface UserRepositoryInterface
{
public function findById(int $id): ?User;
public function findByEmail(string $email): ?User;
public function save(User $user): User;
public function delete(User $user): bool;
}Bu arayüz, havuzun neler yapabileceğini tanımlar. Verilerin nasıl saklandığını veya alındığını tanımlamaz.
Artık somut bir uygulama oluşturabiliriz:
class UserRepository implements UserRepositoryInterface
{
public function findById(int $id): ?User
{
return User::find($id);
}
public function findByEmail(string $email): ?User
{
return User::where('email', $email)->first();
}
public function save(User $user): User
{
$user->save();
return $user;
}
public function delete(User $user): bool
{
return $user->delete();
}
}Uygulama bir ORM modeli kullanır, ancak uygulamanın geri kalanı doğrudan ORM sorgularına bağlı olmak yerine arayüze bağlı olabilir.
Repositoryyu Bir Hizmette Kullanmak
Bir hizmet sınıfı, depoyu bağımlılık enjeksiyonu yoluyla kullanabilir:
class UserRegistrationService
{
public function __construct(
private UserRepositoryInterface $users
) {
}
public function register(array $data): User
{
$existingUser = $this->users->findByEmail($data['email']);
if ($existingUser) {
throw new RuntimeException('Email already exists.');
}
$user = new User();
$user->name = $data['name'];
$user->email = $data['email'];
$user->password = password_hash($data['password'], PASSWORD_BCRYPT);
return $this->users->save($user);
}
}Hizmet artık business logicna odaklanıyor. E-postanın var olup olmadığını kontrol eder, kullanıcı nesnesini oluşturur ve depodan onu kaydetmesini ister. Hizmetin tam database sorgusunu bilmesine gerek yoktur.
Repository Pattern'nun Ana Parçaları
Repository Pattern genellikle şu ana parçaları içerir:
Repository arayüzü: Veri erişimi için mevcut yöntemleri tanımlar.
Somut depo: database, ORM, API veya başka bir veri kaynağı kullanarak arayüzü uygular.
Varlık veya model: Uygulama tarafından kullanılan veri nesnesini temsil eder.
Hizmet veya kullanım durumu: İş operasyonlarını gerçekleştirmek için depoyu kullanır.
Bu yapı, iş davranışını kalıcılık ayrıntılarından ayırmaya yardımcı olur.
Repository Pattern ve Veri Erişim Mantığı
Veri erişim mantığı; sorguları, filtreleri, sayfalandırmayı, joinsi, sıralamayı, kalıcılık işlemlerini ve veri alma kurallarını içerir. Bu işlemler genellikle uygulama büyüdükçe değişir.
Repositorylar bu mantık için özel bir yer sağlar. Örneğin, aktif kullanıcı sorgularını her yerde tekrarlamak yerine UserRepository, getActiveUsers adlı bir yöntem tanımlayabilir.
public function getActiveUsers(): array
{
return User::where('active', true)
->orderBy('created_at', 'desc')
->get()
->all();
}Artık uygulama, sorguyu birçok yerde tekrarlamadan getActiveUsers'ı çağırabilir.
Laravel'da Repository Pattern
Laravel, halihazırda birçok veri erişim yöntemi sağlayan Eloquent ORM'yi kullanır. Bu nedenle bazı geliştiriciler Repository Pattern'nun Laravel'da her zaman gerekli olup olmadığını tartışıyorlar.
Küçük Laravel projelerinde doğrudan Eloquent'i kullanmak yeterli olabilir. Ancak daha büyük uygulamalarda, sorgular karmaşık hale geldiğinde, veri erişiminin ayrı olarak test edilmesi gerektiğinde veya uygulamanın doğrudan Eloquent modelleri yerine arayüzlere bağlı olması gerektiğinde depolar hala yararlı olabilir.
Laravel'daki Repository Pattern, projenin bir hizmet katmanına, karmaşık iş kurallarına, birden fazla veri kaynağına veya clean architecture yaklaşımına sahip olduğu durumlarda en kullanışlıdır.
Laravel Repository Arayüzü Örneği
Bir Laravel projesinde, bir uygulama/Repositorylar veya uygulama/Sözleşmeler klasörünün içine bir depo arayüzü yerleştirilebilir.
namespace App\Repositories;
use App\Models\User;
interface UserRepositoryInterface
{
public function findByEmail(string $email): ?User;
public function create(array $data): User;
public function getActiveUsers(int $limit = 20);
}Arayüz, hizmetler ve denetleyiciler tarafından kullanılan sözleşmeyi tanımlar.
Laravel Repository Uygulama Örneği
namespace App\Repositories;
use App\Models\User;
class EloquentUserRepository implements UserRepositoryInterface
{
public function findByEmail(string $email): ?User
{
return User::where('email', $email)->first();
}
public function create(array $data): User
{
return User::create($data);
}
public function getActiveUsers(int $limit = 20)
{
return User::where('active', true)
->latest()
->paginate($limit);
}
}Bu uygulama Eloquent'i kullanır. Veri kaynağının daha sonra değişmesi durumunda servis arayüzünü değiştirmeden başka bir uygulama oluşturulabilir.
Laravel Hizmet Kapsayıcısındaki Bağlama Havuzu
Laravel'nun hizmet kapsayıcısı, arayüzü somut depo sınıfına bağlayabilir:
use App\Repositories\UserRepositoryInterface;
use App\Repositories\EloquentUserRepository;
public function register(): void
{
$this->app->bind(
UserRepositoryInterface::class,
EloquentUserRepository::class
);
}Bu bağlamanın ardından Laravel, bir sınıf UserRepositoryInterface gerektirdiğinde otomatik olarak EloquentUserRepository'yi enjekte edebilir.
Laravel Hizmetinde Repositoryyu Kullanma
namespace App\Services;
use App\Repositories\UserRepositoryInterface;
use App\Models\User;
class UserService
{
public function __construct(
private UserRepositoryInterface $users
) {
}
public function register(array $data): User
{
if ($this->users->findByEmail($data['email'])) {
throw new \RuntimeException('Email already exists.');
}
return $this->users->create($data);
}
}UserService, depo arayüzüne bağlıdır ve doğrudan Eloquent sorgularından bağımsız kalır.
Symfony'da Repository Pattern
Symfony projeleri sıklıkla Doctrine ORM içeren depoları kullanır. Doktrin depoları Symfony uygulamalarının ortak bir parçasıdır ve bir varlıkla ilgili sorguları düzenlemek için kullanılır.
Örneğin, bir UserRepository findActiveUsers, findByEmail veya findUsersCreatedAfter gibi yöntemler içerebilir. Symfony ve Doctrine doğal olarak depo tarzı organizasyonu destekler.
Daha büyük Symfony uygulamalarında geliştiriciler ayrıca veri havuzu arayüzlerini tanımlayabilir ve iş hizmetlerini Doctrine'e özgü ayrıntılardan ayırmak için bağımlılık enjeksiyonunu kullanabilir.
Ham SQL ile Repository Pattern
Repository Pattern, ORM araçlarıyla sınırlı değildir. Bir depo aynı zamanda ham SQL’i, sorgu oluşturucuları, stored procedures'yu, harici APIs'yu veya dosyaları da kullanabilir.
Örneğin, bir ürün deposu PDO'yu doğrudan kullanabilir:
class PdoProductRepository implements ProductRepositoryInterface
{
public function __construct(
private PDO $pdo
) {
}
public function findById(int $id): ?Product
{
$statement = $this->pdo->prepare('SELECT * FROM products WHERE id = :id');
$statement->execute(['id' => $id]);
$data = $statement->fetch(PDO::FETCH_ASSOC);
return $data ? Product::fromArray($data) : null;
}
}İş mantığı hala ProductRepositoryInterface'e bağlıdır ve uygulamanın PDO, Eloquent, Doctrine veya API kullanıp kullanmadığını umursamaz.
Harici APIs ile Repository Pattern
Repositorylar ayrıca harici API erişimini de gizleyebilir. Örneğin, bir Müşteri Repositorysu, müşteri verilerini yerel bir database yerine bir CRM API'dan alabilir.
class ApiCustomerRepository implements CustomerRepositoryInterface
{
public function __construct(
private CrmApiClient $client
) {
}
public function findByEmail(string $email): ?Customer
{
$response = $this->client->get('/customers', [
'email' => $email,
]);
return Customer::fromApiResponse($response);
}
}Uygulamanın geri kalanı aynı depo arayüzünü kullanır. Veri kaynağı havuzun arkasında gizlidir.
Repository Pattern ve Service Layer
Repository Pattern ve Service Layer Modeli birbiriyle ilişkilidir ancak farklıdır.
Bir depo veri erişimini yönetir. Verilerin nasıl bulunacağını, kaydedileceğini, güncelleneceğini ve silineceğini bilir. Bir hizmet business logicnı yönetir. Repositoryları koordine eder, kuralları uygular, iş akışlarını doğrular ve işlemleri gerçekleştirir.
Örneğin, UserRepository bir kullanıcıyı e-posta yoluyla bulabilir. UserRegistrationService, e-postanın zaten mevcut olup olmadığını kontrol edebilir, şifreyi karma haline getirebilir, kullanıcıyı oluşturabilir, bir doğrulama e-postası gönderebilir ve sonucu döndürebilir.
Repositorylar ticari hizmet haline gelmemelidir. Bu ayrımı net tutmak sürdürülebilirliği artırır.
Repository Pattern ve DAO
DAO, Veri Erişim Nesnesi anlamına gelir. DAO ve Repository benzerdir çünkü her ikisi de veri erişim mantığını düzenler. Ancak genellikle biraz farklı amaçlarla kullanılırlar.
DAO genellikle database'ya daha yakındır ve kalıcılık işlemlerine odaklanır. Tablolarla ve sorgularla yakından eşleşen yöntemleri ortaya çıkarabilir.
Bir depo genellikle daha etki alanı odaklıdır. collection etki alanı nesnelerini temsil eder ve findActiveCustomers veya getRecentOrders gibi uygulama için anlamlı yöntemler sağlar.
Uygulamada birçok proje bu terimleri benzer şekillerde kullanır ancak Repository Pattern genellikle etki alanı odaklı tasarım ve iş odaklı veri erişimiyle daha bağlantılıdır.
Repository Pattern ve Aktif Kayıt Karşılaştırması
Aktif Kayıt, model nesnesinin hem verileri hem de database işlemlerini içerdiği bir modeldir. Laravel Eloquent, Aktif Kayıt'ın bir örneğidir.
Aktif Kayıt ile geliştiriciler User::where(...), User::create(...) veya $user->save() yazabilirler. Bu basit ve üretkendir.
Repository Pattern, veri erişim mantığını ayrı bir depo sınıfına yerleştirir. Bu daha fazla yapı ve soyutlama ekleyebilir.
Laravel'da Eloquent'in üzerinde depoların kullanılması bir tasarım tercihidir. Büyük projeler için yararlı olabilir ancak küçük CRUD uygulamaları için gereksiz olabilir.
Repository Pattern ve Temiz Mimari
Repository Pattern, clean architecture'da önemlidir çünkü business logicnı dış veri kaynaklarından bağımsız tutmaya yardımcı olur. Temel uygulama, veri havuzu arayüzlerini tanımlayabilirken altyapı kodu somut uygulamalar sağlar.
Örneğin, uygulama katmanı OrderRepositoryInterface'i tanımlayabilir. Altyapı katmanı bunu MySQL, PostgreSQL, MongoDB veya harici bir API kullanarak uygulayabilir.
Bu, database teknolojisi değişse bile business logicnın sabit kalmasını sağlar.
Repository Pattern ve Test
Repository Pattern'nun en büyük faydalarından biri gelişmiş test edilebilirliktir. Bir hizmet bir veri havuzu arayüzüne bağlıysa testler, gerçek bir database kullanmak yerine sahte bir veri havuzu sağlayabilir.
class FakeUserRepository implements UserRepositoryInterface
{
private array $users = [];
public function findByEmail(string $email): ?User
{
foreach ($this->users as $user) {
if ($user->email === $email) {
return $user;
}
}
return null;
}
public function create(array $data): User
{
$user = new User($data);
$this->users[] = $user;
return $user;
}
}Bu, birim testlerinin daha hızlı ve daha odaklı olmasını sağlar çünkü gerçek bir database'ya bağlanmaları gerekmez.
Ortak Repository Yöntemleri
Repositorylar genellikle veri erişimi için ortak yöntemler içerir. Kesin yöntemler projeye ve varlığa bağlıdır.
Yaygın depo yöntemleri şunları içerir:
FindById
FindByEmail
Tümünü Bul
yarat
kaydet
güncelleme
sil
sayfalara ayırmak
aktif ol
FindByStatus
Ancak depolar genel çöplük haline gelmemelidir. Yöntemler uygulama için anlamlı olmalıdır.
Genel Repository ve Özel Repository
Bazı geliştiriciler, tüm modeller için ortak CRUD yöntemleriyle genel bir depo oluşturur. Bu, çoğaltmayı azaltabilir ancak aynı zamanda alana özgü önemli davranışları da gizleyebilir.
UserRepository veya OrderRepository gibi belirli bir depo, o varlıkla ilgili anlamlı yöntemler içerebilir.
Örneğin, OrderRepository'de findPendingOrders, findPaidOrders veya getOrdersForCustomer bulunabilir. Bu yöntemler her yerde genel filtreler kullanmaktan daha açıktır.
Birçok gerçek projede, belirli depolar, tek bir büyük genel depodan daha anlamlıdır ve anlaşılması daha kolaydır.
Repository Pattern'nun Faydaları
Repository Pattern, nesne yönelimli yazılım geliştirmede birçok fayda sağlar.
Ana faydalar şunları içerir:
Veri erişim mantığını business logicndan ayırır.
Denetleyicileri ve hizmetleri daha temiz tutar.
Sorguları ve kalıcılık işlemlerini merkezileştirir.
Arayüzler ve sahte depolar aracılığıyla test edilebilirliği artırır.
database sorgularının yinelenmesini azaltır.
Veri kaynaklarını değiştirmeyi kolaylaştırır.
clean architecture'yu ve etki alanı odaklı tasarımı destekler.
Büyük uygulamalarda sürdürülebilirliği artırır.
Bu avantajlar özellikle karmaşık veri erişim gereksinimleri olan projelerde değerlidir.
Repository Pattern'nun dezavantajları
Repository Pattern'nun dezavantajları da olabilir. Basit uygulamalar için gereksiz olabilecek ekstra sınıflar ve arayüzler ekler.
Laravel gibi çerçevelerde Eloquent zaten güçlü bir Aktif Kayıt APIsağlıyor. Her model için depo eklemek, gerçek değer eklemeden yalnızca Eloquent'i saran yinelenen yöntemler oluşturabilir.
Diğer bir dezavantaj ise aşırı soyutlamadır. Repositorylar kötü tasarlanmışsa basit sorguları gerekenden daha karmaşık hale getirebilirler.
Repository Pattern her projede otomatik olarak değil, yapıyı iyileştirdiğinde kullanılmalıdır.
Repository Pattern Ne Zaman Kullanılır?
Veri erişim mantığı karmaşık, tekrarlanan veya business logicndan ayrılacak kadar önemli olduğunda Repository Pattern'yu kullanın.
Repository Pattern şu durumlarda faydalıdır:
Denetleyiciler veya hizmetler çok fazla database sorgusu içeriyor.
Aynı sorgular birçok yerde tekrarlanıyor.
Uygulamanın business logic ile kalıcılık arasında net bir ayrım yapması gerekiyor.
Hizmetleri gerçek bir database kullanmadan test etmek istiyorsunuz.
Proje clean architecture veya etki alanı odaklı tasarımı kullanıyor.
Uygulama gelecekte veri kaynaklarını değiştirebilir.
Sorgular karmaşıktır ve tek bir yerde düzenlenmelidir.
Bu koşullar mevcutsa Repository Pattern sürdürülebilirliği ve test edilebilirliği geliştirebilir.
Repository Pattern Ne Zaman Kullanılmamalı?
Her basit CRUD özelliği için Repository Pattern'yu otomatik olarak kullanmayın. Proje küçükse ve Eloquent veya başka bir ORM zaten net veri erişimi sağlıyorsa, depo eklemek gerekli olmayabilir.
Aşağıdaki durumlarda Repository Pattern'dan kaçının:
Uygulama küçük ve basittir.
Repository yöntemleri yalnızca ORM yöntemlerini değer eklemeden kopyalar.
Soyutlama kodun anlaşılmasını zorlaştırır.
Projenin sahte depolar aracılığıyla test edilmesine gerek yoktur.
Veri erişim mantığı minimum düzeydedir ve değişmesi olası değildir.
İyi tasarım pratik olmalıdır. Repository Pattern, gerçek bir organizasyon veya test problemini çözdüğünde kullanışlıdır.
Repository Pattern ile Yapılan Yaygın Hatalar
Yaygın bir hata, business logicnı depoların içine koymaktır. Bir veri havuzu, iş iş akışlarını değil, veri erişimini yönetmelidir.
Diğer bir hata ise anlamlı etki alanı yöntemlerini gizleyen genel depolar oluşturmaktır. FindActiveUsers gibi bir yöntem genellikle her yerde kullanılan genel bir findByConditions yönteminden daha anlaşılırdır.
Üçüncü bir hata, değer katmadan yalnızca basit ORM çağrılarını saran depolar oluşturmaktır. Bu, tasarımı iyileştirmeden kod boyutunu artırır.
Dördüncü hata ise tutarsız veri türlerinin döndürülmesidir. Repositorylama yöntemleri öngörülebilir olmalı ve açıkça belgelenmelidir.
Repository Pattern için En İyi Uygulamalar
Repository Pattern'yu etkili bir şekilde kullanmak için geliştiricilerin depoları odaklanmış ve anlamlı tutması gerekir.
Yararlı en iyi uygulamalar şunları içerir:
Gerçek veri erişim mantığını düzenlemek için depoları kullanın.
İş kurallarını depolarda değil hizmetlerde veya etki alanı sınıflarında tutun.
Test edilebilirlik veya ayrıştırma gerektiğinde arayüzleri tanımlayın.
Uygulama ihtiyaçlarına göre anlamlı yöntem adları kullanın.
Yalnızca ORM yöntemlerini kopyalayan depolar oluşturmaktan kaçının.
Sorgu mantığını merkezi ve yeniden kullanılabilir tutun.
Repository yöntemlerinden tutarlı türleri döndürün.
Hizmetlere depo sağlamak için bağımlılık eklemeyi kullanın.
Bu uygulamalar, veri havuzlarının gereksiz karmaşıklık eklemek yerine tasarımı geliştirmesine yardımcı olur.
Repository Pattern Kullanmadan Önce Pratik Kontrol Listesi
Repository Pattern'yu kullanmadan önce geliştiriciler şu soruları sorabilir:
Veri erişim mantığı birden çok yerde tekrarlanıyor mu?
Denetleyiciler veya hizmetler çok fazla sorguyla mı dolu?
Gerçek bir database olmadan business logicnı test etmem gerekiyor mu?
ORM veya database ayrıntılarını gizlemem gerekir mi?
Sorgular özel bir sınıfı hak edecek kadar karmaşık mı?
Repository yöntemleri kodu daha okunabilir hale getirecek mi?
Soyutlamayı gerçek bir nedenden dolayı mı ekliyorum?
Bu soruların birçoğunun cevabı evet ise Repository Pattern iyi bir tasarım tercihi olabilir.
Sonuç
Repository Pattern, veri erişim mantığını business logicndan ayıran bir tasarım modelidir. Denetleyicilerin ve hizmetlerin uygulama davranışına odaklanmasını sağlarken verileri almak, depolamak, güncellemek ve silmek için temiz bir katman sağlar.
Repository Pattern karmaşık uygulamalarda, APIs, Laravel projelerinde, Symfony uygulamalarında, clean architecture'da ve etki alanı odaklı tasarımda kullanışlıdır. Organizasyonu geliştirir, yinelenen sorguları azaltır, bağımlılık enjeksiyonunu destekler ve sahte uygulamalar yoluyla testi kolaylaştırır.
Ancak depoların dikkatli kullanılması gerekir. Küçük uygulamalarda veya basit CRUD sistemlerinde depoların eklenmesi gereksiz soyutlama yaratabilir. Doğru uygulandığında Repository Pattern temiz, bakımı yapılabilir ve ölçeklenebilir nesne yönelimli yazılım oluşturmaya yönelik güçlü bir araçtır.

