İçeriğe geç
Zeynep Baş — Ana sayfa
Tüm yazılar
  • Design Pattern

Design Pattern Kullanmak Her Zaman İyi Bir Fikir mi?

Design Pattern'ler kodu otomatik olarak daha iyi hale getirmez. Asıl önemli olan hangi problemi çözdüğünü, ne zaman kullanılması gerektiğini ve ne zaman kullanılmaması gerektiğini bilmektir.

Zeynep Baş6 dk okuma

Design Pattern Nedir?

Tekrar eden yazılım problemlerine yönelik kanıtlanmış çözüm yaklaşımları

Design Pattern, belirli bir yazılım problemini çözmek için kullanılan tekrar edilebilir bir tasarım yaklaşımıdır. Pattern doğrudan kopyalanacak bir kod parçası değil, probleme nasıl yaklaşılması gerektiğini anlatan bir rehberdir.

  • Tekrarlayan problemlere çözüm

  • Kod organizasyonu

  • Bağımlılıkların yönetimi

  • Değişime açık mimari

  • Sorumlulukların ayrıştırılması

Bir pattern kullanmak tek başına kodun daha kaliteli olduğu anlamına gelmez. Pattern'in gerçekten bir problemi çözüp çözmediği ve sisteme eklediği karmaşıklığın buna değip değmediği değerlendirilmelidir.

  • Problem varsa pattern düşünülmeli

  • Gereksiz abstraction oluşturulmamalı

  • Kod okunabilirliği korunmalı

  • Bakım maliyeti değerlendirilmelidir

Pattern Kullanmak Her Zaman İyi Bir Fikir mi?

Hayır. Pattern bir amaç değil, araçtır.

Bir geliştiricinin bildiği her pattern'i projede kullanmaya çalışması genellikle daha iyi bir architecture oluşturmaz. Aksine gereksiz abstraction katmanları oluşturarak kodun anlaşılmasını ve bakımını zorlaştırabilir.

  • Her problem Strategy gerektirmez

  • Her API Repository gerektirmez

  • Her state değişimi Observer gerektirmez

  • Her HTTP client Singleton olmak zorunda değildir

Senior seviyede önemli olan kaç pattern bildiğiniz değil, bir problemin pattern gerektirip gerektirmediğini değerlendirebilmektir.

  • Problem odaklı düşünmek

  • Trade-off değerlendirmek

  • Karmaşıklığı kontrol etmek

  • Gelecekteki değişiklikleri öngörmek

Overengineering Nedir?

Basit bir problemi gereğinden fazla karmaşık hale getirmek

Overengineering, henüz ihtiyaç olmayan bir esnekliği veya abstraction'ı sisteme erkenden eklemektir. Kod teorik olarak daha esnek hale gelirken pratikte okunması ve değiştirilmesi daha zor olabilir.

  • Gereksiz abstraction

  • Fazla interface

  • Gereksiz provider katmanları

  • Fazla dependency

  • Karmaşık klasör yapısı

Örneğin yalnızca iki farklı davranış bulunan basit bir component için Strategy Pattern oluşturmak teknik olarak mümkün olsa da mevcut problem bunu gerektirmiyorsa gereksiz bir karmaşıklık oluşturabilir.

  • Önce basit çözüm

  • Değişim ihtiyacını gözlemle

  • Tekrarlayan yapı oluşursa abstraction düşün

  • Pattern'i ihtiyaç ortaya çıktığında uygula

Strategy Pattern

Değişebilen algoritmaları birbirinden ayırmak

Strategy Pattern, aynı işi farklı yöntemlerle gerçekleştiren algoritmaları birbirinden ayırmayı sağlar. Böylece ana business logic, hangi algoritmanın kullanılacağına bağlı olmaktan çıkar.

  • Filtreleme stratejileri

  • Sıralama stratejileri

  • Validation stratejileri

  • Ödeme yöntemleri

  • Farklı hesaplama yöntemleri

React uygulamalarında farklı filtreleme veya sıralama davranışları zamanla artıyorsa Strategy Pattern tekrar eden koşulların ve büyük if-else bloklarının önüne geçebilir.

  • Strategy seçimi

  • Business logic ayrıştırma

  • Kolay test edilebilirlik

  • Yeni davranış ekleme kolaylığı

Provider Pattern

Bağımlılıkları component ağacında yönetmek

Provider Pattern, belirli bir değeri veya servisi component ağacındaki alt component'lere ulaştırmak için kullanılır. React Context API bu yaklaşımın en bilinen örneklerinden biridir.

  • Authentication

  • Theme

  • Configuration

  • Feature Flags

  • Global state

Provider kullanırken dikkat edilmesi gereken nokta, her state'i global hale getirmemektir. Gereksiz provider'lar component ağacını karmaşıklaştırabilir ve state yönetimini zorlaştırabilir.

  • Global olması gereken state'i seç

  • Local state'i local tut

  • Provider sayısını kontrol et

  • Dependency scope'unu düşün

Repository Pattern

Data access ile business logic'i birbirinden ayırmak

Repository Pattern, veri kaynağına erişimi uygulamanın geri kalanından soyutlar. Component veya business logic doğrudan fetch, Axios veya başka bir data source ile konuşmak yerine repository üzerinden veri alabilir.

  • API abstraction

  • Data access

  • Mock data

  • Test edilebilirlik

  • Backend değişikliklerine karşı izolasyon

Örneğin PDOS gibi bir uygulamada TaskRepository üzerinden task verilerine erişmek, component'lerin API detaylarını bilmesini engeller.

  • TaskRepository

  • DocumentRepository

  • GoalRepository

  • ProfileRepository

Observer Pattern

Bir değişiklik olduğunda ilgili yapıları haberdar etmek

Observer Pattern, bir nesnede meydana gelen değişikliğin ona bağlı diğer yapılara bildirilmesini sağlar. Event-driven sistemlerde ve reactive yapılarda sıkça karşılaşılan bir yaklaşımdır.

  • Event sistemleri

  • Notification

  • WebSocket

  • Realtime updates

  • State değişiklikleri

Frontend tarafında Observer yaklaşımını event listener yapılarında, browser API'lerinde veya realtime veri akışlarında görebiliriz.

  • subscribe

  • unsubscribe

  • event listener

  • WebSocket events

Adapter Pattern

Uyumsuz veri yapılarını birbirine adapte etmek

Adapter Pattern, bir sistemden gelen veriyi uygulamanın ihtiyaç duyduğu formata dönüştürmek için kullanılır. Özellikle backend response formatı ile frontend domain modelinin birbirinden farklı olduğu durumlarda oldukça kullanışlıdır.

  • API response mapping

  • Legacy API

  • Third-party services

  • Domain model dönüşümü

Backend'in döndürdüğü alanları doğrudan component içerisinde dönüştürmek yerine adapter katmanında normalize etmek UI tarafının daha sade kalmasını sağlar.

  • API model

  • Adapter

  • Domain model

  • UI model

Singleton Pattern

Uygulama içerisinde tek bir instance üzerinden çalışmak

Singleton Pattern, belirli bir servisin veya nesnenin uygulama içerisinde tek bir instance ile kullanılmasını amaçlar. Frontend uygulamalarında API client veya configuration gibi yapılarda karşımıza çıkabilir.

  • API Client

  • Axios instance

  • Configuration

  • Logger

Ancak Singleton kullanımı da otomatik olarak doğru değildir. Global mutable state oluşturmak test edilebilirlik ve bağımlılık yönetimi açısından sorunlara yol açabilir.

  • Global state riski

  • Test izolasyonu

  • Dependency yönetimi

  • Instance lifecycle

React ve Next.js'te Pattern'ler

Pattern'ler framework'ten bağımsızdır ancak kullanım şekli değişebilir

React ve Next.js uygulamalarında Design Pattern'ler genellikle component, service, data access ve state management katmanlarında karşımıza çıkar.

  • Component architecture

  • Service layer

  • Repository layer

  • State management

  • Data fetching

Next.js App Router ile Server Component ve Client Component ayrımı da architecture kararlarını doğrudan etkiler. Bu nedenle pattern seçimi yalnızca teorik olarak değil, framework'ün çalışma modelini dikkate alarak yapılmalıdır.

  • Server Components

  • Client Components

  • Server Actions

  • Caching

  • Data Fetching

  • Route Handlers

Gerçek Bir Next.js Senaryosu

PDOS gibi büyüyen bir uygulamada pattern seçimi

Bir task management uygulamasında başlangıçta component içerisinden doğrudan API çağrısı yapmak yeterli olabilir. Ancak uygulama büyüdükçe aynı data access logic'i farklı feature'larda tekrar etmeye başlayabilir.

  • Tasks

  • Goals

  • Documents

  • Calendar

  • Analytics

Bu noktada Repository Pattern ile data access katmanı ayrılabilir. Farklı task işlemleri Strategy Pattern ile yönetilebilir ve API response'ları Adapter katmanında frontend modeline dönüştürülebilir.

  • Repository

  • Strategy

  • Adapter

  • Service

  • UI

Ancak uygulama küçükken tüm bu katmanları oluşturmak yerine basit bir servis yapısıyla başlamak daha doğru olabilir. Architecture ihtiyaç ortaya çıktıkça evrilmelidir.

  • YAGNI

  • Incremental architecture

  • Low coupling

  • High cohesion

  • Maintainability

Pattern Kullanmamak Ne Zaman Daha Doğru?

Basitlik de bir architecture kararıdır

Eğer problem basitse, çözümün de basit olması genellikle daha sağlıklıdır. Tek bir API çağrısı için Repository, tek bir davranış için Strategy veya tek bir component için Facade oluşturmak gereksiz olabilir.

  • Küçük component

  • Tek API çağrısı

  • Tek davranış

  • Düşük değişim ihtiyacı

  • Düşük domain complexity

Pattern kullanmadan önce şu soruyu sormak gerekir: Bu abstraction bugün gerçekten bir problemi çözüyor mu, yoksa gelecekte belki lazım olur diye mi oluşturuluyor?

  • Bugünkü problemi tanımla

  • Değişim ihtimalini değerlendir

  • Complexity maliyetini hesapla

  • Gerekirse daha sonra abstraction ekle

Pattern Seçerken Sorulması Gereken Sorular

Pattern uygulamadan önce problemi doğru tanımla

Bir pattern uygulamadan önce yalnızca 'Bu pattern'i kullanabilir miyim?' sorusu yerine problemin gerçekten abstraction gerektirip gerektirmediği değerlendirilmelidir.

  • Hangi problem çözülüyor?

  • Bu problem tekrar ediyor mu?

  • Kod değişmeye açık mı?

  • Yeni davranışlar eklenecek mi?

  • Test edilebilirlik etkileniyor mu?

Sonuç

Pattern değil, problem odaklı düşünmek

Design Pattern'ler frontend architecture'ın önemli araçlarından biridir. Ancak pattern kullanmak başlı başına bir kalite göstergesi değildir. Önemli olan hangi problemin çözüldüğünü ve seçilen abstraction'ın gerçekten değer üretip üretmediğini bilmektir.

  • Pattern bir araçtır

  • Problem önce gelir

  • Basitlik değerlidir

  • Overengineering'den kaçınılmalıdır

  • Architecture zamanla gelişebilir

React ve Next.js projelerinde Strategy, Provider, Repository, Observer, Adapter, Facade ve Singleton gibi pattern'ler doğru problemlerle karşılaşıldığında oldukça güçlü çözümler sunabilir. Fakat iyi bir frontend architecture, pattern sayısıyla değil, doğru abstraction ve doğru trade-off'larla oluşturulur.

  • Doğru abstraction

  • Doğru trade-off

  • Maintainability

  • Scalability

  • Developer experience

Kısacası senior yaklaşım, 'Hangi pattern'i kullanmalıyım?' sorusundan önce 'Buradaki gerçek problem ne?' sorusunu sormaktır.

  • Önce problemi anla

  • Sonra çözümü tasarla

  • Pattern gerekiyorsa uygula

  • Gerekmiyorsa basit bırak

Diğer yazılar