Adımızı SOLID'den aldık; bu yüzden en sık duyduğumuz soru da şu: "Bu prensipler gerçekten işe yarıyor mu, yoksa sadece güzel bir teori mi?" Kısa cevap: Prensipler kendi başına bir şey kazandırmaz. Kazandıran, değişimin maliyetini düşürmeleridir. Bir yazılımın ömrünün büyük kısmı ilk yayından sonra geçer ve o dönemde en pahalı şey, "küçük bir değişikliğin" beklenmedik yerleri kırmasıdır.
Aşağıdaki örnekler, ödeme alan bir sipariş akışından sadeleştirilerek alındı.
S — Tek sorumluluk: Bir sınıf, değişmek için tek bir sebep
İlk sürümde OrderService siparişi kaydediyor, ödemeyi alıyor, fatura e-postasını gönderiyor ve stok düşüyordu. Muhasebe e-posta şablonunu değiştirmek istediğinde, ödeme koduna da dokunan bir dosyayı açmak zorunda kaldık.
Çözüm, sınıfları değişim sebebine göre ayırmaktı: OrderRepository kayıttan, PaymentGateway ödemeden, InvoiceMailer e-postadan sorumlu. Artık şablon değişikliği yalnızca bir dosyayı ilgilendiriyor ve testleri de o kadar küçük.
O — Açık/kapalı: Genişlemeye açık, değişikliğe kapalı
Müşterimiz iyzico'dan sonra Stripe da istediğinde, if (provider === 'stripe') dalları eklemek en kolay yoldu. Bunun yerine ödeme işini bir arayüzün arkasına aldık:
interface PaymentGateway {
charge(order: Order): Promise<Receipt>;
}
class IyzicoGateway implements PaymentGateway { /* ... */ }
class StripeGateway implements PaymentGateway { /* ... */ }Yeni sağlayıcı yeni bir sınıf demek; ödeme akışının kendisi değişmiyor. Riskli olan, zaten çalışan koda dokunmak; bu yapı tam olarak bunu engelliyor.
L — Liskov: Alt sınıf, üst sınıfın sözünü tutmalı
Bir "test modu" ağ geçidi, ödeme başarısız olduğunda hata fırlatmak yerine null dönüyordu. Arayüz Receipt söz veriyordu; bu sınıf sözü bozdu ve sonucu kontrol etmeyen bir çağıran üretimde sessizce yanlış davrandı. Kural basit: Arayüzün yerine geçen her sınıf, aynı sözleşmeyi aynı şekilde yerine getirmeli. Bunu da her uygulamaya aynı test setini koşturarak güvenceye alıyoruz.
I — Arayüz ayrımı: Kimse kullanmadığı metoda bağımlı olmasın
Başlangıçtaki MediaStorage arayüzünde yükleme, silme, imzalı URL ve görsel dönüştürme vardı. Sadece okuma yapan rapor servisi bile tüm bu metotları "biliyordu". Arayüzü ObjectReader ve ObjectWriter olarak böldük; her tüketici yalnızca ihtiyacını görüyor, test için sahte nesne yazmak da iki satıra indi.
D — Bağımlılığın tersine çevrilmesi: İş kuralları altyapıyı tanımasın
En çok fark yaratan prensip bu oldu. Alan katmanımız (domain) Prisma'yı, NestJS'i ya da S3'ü import etmiyor; yalnızca kendi tanımladığı soyut depo ve servis arayüzlerini kullanıyor. Somut sınıflar dış katmanda yaşıyor ve uygulama açılırken bağlanıyor.
Sonuç: İş kurallarının testleri veritabanı olmadan milisaniyeler içinde çalışıyor. Veritabanı sürümünü yükseltmek ya da depolamayı değiştirmek iş kurallarına dokunmadan yapılabiliyor.
Ne zaman fazla gelir?
SOLID bir bürokrasi değil. Tek seferlik bir betikte üç arayüz yazmak israftır. Bizim pratik ölçütümüz şu:
Bir kod parçasının ikinci bir uygulaması gerçekten konuşuluyorsa, arayüzü şimdi çıkarın.
Bir dosya, birbirinden bağımsız iki ekibin isteğiyle değişiyorsa, bölün.
Bir testi yazmak için gerçek bir veritabanı gerekiyorsa, bağımlılık yanlış yöndedir.
İyi mimarinin ölçüsü, ilk gün ne kadar şık göründüğü değil; ikinci yıl yapılan değişikliğin ne kadar sıkıcı geçtiğidir.
Projenizde değişikliklerin neden bu kadar uzun sürdüğünü merak ediyorsanız, kod tabanınıza birlikte bakabiliriz.


