Factory Pattern'inin Ölümü: Saf Fonksiyonlarla Node.js Kodumuzun 40%'ını Nasıl Sildik
Node.js microservice'lerimizden tüm factory'leri, service'leri ve dependency injection'ları çıkardıktan sonra, 65% daha az bug ile 3x daha hızlı ship etmeye başladık. Event-driven mimariler için fonksiyonların sınıfları neden geçtiğini anlatıyorum.
Hiçbir İşe Yaramayan 847 Satırlık Service
20 dakika sürmesi gereken bir ödeme işleme bug'ını debug ediyordum. Bunun yerine, tek bir validation kuralını değiştirmek için 847 satır "kurumsal mimari"de 3 saat geçirdim.
Suçlu? PaymentService sınıfımız—bir factory, dependency injection, 12 farklı interface ve bir Java developer'ı sevinçle ağlatacak kadar soyutlama içeren aşırı mühendislik şaheseri. Event-driven Lambda mimarisinde pure function'lar bu tür class yapılarını geçer: daha az boilerplate, daha kolay test, daha hızlı iterasyon.
Düzeltmeye çalıştığım bug? Basit bir validation: "Kredi kartı numaraları boşluk içermemeli."
Saf fonksiyonlar ve event-driven mimariye geçişimizden sonra, aynı fonksiyonalite şöyle oldu:
Bug fix süresi: 3 saat → 15 dakika. Kod satırı: 847 → 89. Test karmaşıklığı: 156 test case → 12 test case.
Bu hikaye, factory'lerimizi, service'lerimizi ve dependency injection container'larımızı nasıl öldürdüğümüzü ve Node.js uygulamalarımızın neden daha hızlı, daha güvenilir ve çalışması sonsuz derecede daha keyifli hale geldiğini anlatıyor.
Node.js'in Java-laşması: Nasıl Bu Noktaya Geldik
Microservice'ler inşa etmeye başladığımızda, Java ve C# geçmişinden geliyorduk. O ekosistemlerde mantıklı olan enterprise pattern'leri getirdik:
- "Test edilebilirlik" için Dependency Injection
- "Esneklik" için Factory pattern'leri
- "Kaygıların ayrılması" için Service katmanları
- "Veri soyutlaması" için Repository pattern'leri
Zamanla, Node.js kod tabanımız idiomatic JavaScript'ten çok Spring Boot'a benziyordu. Her basit operasyon birden fazla soyutlama katmanında gezinmeyi gerektiriyordu:
Uyanış çağrısı: "Sipariş onay e-postası gönder" feature'ını eklemek birden fazla dosya ve service'te değişiklik gerektirdi. Dependency graph'ını anlamak yeni takım üyeleri için önemli bir zorluk haline geldi.
Event-Driven Aydınlanma
Çıkış noktası, "service"lerimizin çoğunun aslında kılık değiştirmiş event handler'lar olduğunu fark etmemizle geldi.
Synchronous service çağrıları yerine:
Aynı flow'u event'ler olarak modelleyebilirdik:
İçgörü: Her operasyon bir event handler ise, neden hiç sınıfa ihtiyacımız olsun ki?
Büyük Refactoring: Sınıflardan Fonksiyonlara
Aşama 1: Saf Operasyonları Belirle (Hafta 1)
Şu özelliklere sahip operasyonları belirlemeye başladık:
- Stateless (instance variable'ları yok)
- Side-effect free (database/API çağrıları hariç)
- Kolayca test edilebilir (input → output)
Aşama 2: Event Handler Fonksiyonları (Hafta 2)
Her Lambda fonksiyonu basit bir event handler haline geldi:
Aşama 3: Dependency Injection'ı Yok Et (Hafta 3)
Dependency inject etmek yerine, konfigürasyon fonksiyonları ve environment-based switching kullandık:
Sonuçlar: Ölçekte Basitlik
Birkaç haftalık refactoring sonrasında, metriklerimiz anlamlı iyileştirmeler gösterdi:
Kod Azalması
Geliştirme Hızı
Bug Azalması
Ayrıca production bug'larında azalma da gördük. Neden?
- Saf fonksiyonlar öngörülebilir: Aynı input her zaman aynı output üretir
- Gizli state yok: Tutarsız duruma girebilecek instance variable'lar yok
- Kolay test: Karmaşık dependency graph'ları değil, sadece external çağrıları mock et
- Net veri akışı: Event'ler sistem davranışını açık hale getirir
Ortaya Çıkan Pattern'ler
1. Event Handler Pattern
Her Lambda fonksiyonu aynı basit pattern'i takip eder:
2. Saf Business Logic
Tüm business logic saf fonksiyonlar haline geldi:
3. Injection Yerine Konfigürasyon
Dependency injection yerine, environment-based konfigürasyon kullandık:
Test: Kabusdan Keyfе
Önce: Mock Cehennemi
Sonra: Saf Fonksiyon Cenneti
Test iyileştirmeleri: Daha az test case gerekli, daha az mock setup.
Monitoring Devrimi
Saf fonksiyonlar ve event'ler ile monitoring neredeyse kolay hale geldi:
Observability iyileştirmeleri:
- Debug süresi: Ortalama debug session'ları önemli ölçüde azaldı
- Mean Time to Detection: Daha hızlı incident tespiti
- Root cause tanımlama: Gelişmiş tracing yetenekleri
- Performance monitoring: X-Ray ile built-in
Bu Pattern'i NE ZAMAN Kullanmamalısınız
Bu fonksiyonel, event-driven yaklaşım her zaman cevap değil. İşte sınıfları kullanmaya devam edeceğiniz durumlar:
1. Stateful Operasyonlar
2. Karmaşık Lifecycle Management
3. Framework Entegrasyonu
18 Aylık Sonuçlar
Fonksiyonel, event-driven mimariyi benimsedikten sonra:
Takım Verimliliği
- Yeni developer onboarding: Haftalardan günlere azaldı
- Feature teslimat süresi: Geliştirme döngülerinde anlamlı iyileştirme
- Deployment sıklığı: Daha sık, daha güvenli deployment'lar
- Code review süresi: Review karmaşıklığında gözle görülür azalma
Sistem Güvenilirliği
- Production incident'lar: Aylık incident'larda önemli azalma
- Bug fix süresi: Sorunların daha hızlı çözülmesi
- System uptime: Genel güvenilirlikte iyileşme
- Performance: Tüm alanlarda daha iyi response süreleri
İş Etkisi
İş etkisi anlamlıydı:
- Geliştirme verimliliği: Takımlar feature'ları daha hızlı teslim edebildi
- Altyapı maliyetleri: Serverless fonksiyonlar operasyonel yükü azalttı
- Kalite iyileştirmeleri: Daha az production sorunu ve daha hızlı çözüm
- Developer memnuniyeti: Daha basit kod tabanı takım moralini iyileştirdi
Kilit İçgörü: Basitlik Ölçeklenir
Bu yolculuktan çıkan en önemli ders teknik değil, felsefi bir dersdi. Karmaşıklık sofistikasyon değildir.
Java ve C#'ta öğrendiğimiz pattern'ler o bağlamlarda mantıklıydı, ama Node.js fonksiyonel doğasını benimsediğinizde parlar:
- Stateless operasyonlar için sınıflar yerine fonksiyonlar
- Service iletişimi için method çağrıları yerine event'ler
- Dependency'ler için injection yerine konfigürasyon
- Business logic için karmaşık soyutlamalar yerine saf fonksiyonlar
Sıradaki: Node.js Mimarisinin Geleceği
Serverless devrimi bize çoğu enterprise pattern'in aşırı mühendislik olduğunu öğretti. Node.js uygulamalarının geleceği:
- Function-first: Her operasyon küçük, odaklanmış bir fonksiyon olarak
- Event-driven: Varsayılan olarak asynchronous iletişim
- Stateless: Operasyonlar arası paylaşılan mutable state yok
- Observable: Built-in tracing ve monitoring
Sophisticated, ölçeklenebilir sistemleri factory'ler, dependency injection ya da karmaşık sınıf hiyerarşileri olmadan inşa edebileceğinizi kanıtladık. Bazen en iyi mimari yolunuzdan çekilen mimaridir.
Bir dahaki sefer ServiceFactory yaratırken ya da tek implementation'ı olan bir interface yazarken kendinize sorun: "Bu karmaşıklığa ihtiyacım var mı, yoksa JavaScript'te Java'yı yeniden mi yaratıyorum?"
Cevap sizi şaşırtabilir.