Microservices Mimarisinde BFF Pattern: Sahadan Gerçek Hikayeler

Merhaba arkadaşlar,

Bu makalede uzun zamandır yazmak istediğim Backend For Frontend (BFF) pattern’ine değineceğim. Ama bildiğiniz “BFF nedir, tanımı şudur” tarzı bir makale olmayacak bu. Çünkü internette zaten onlarca teorik kaynak var. Ben daha çok sahada yaşadığım deneyimleri, yaptığım hataları ve “keşke en baştan bilseydim” dediğim şeyleri paylaşacağım.

Acı Hikayesiyle Başlayalım

Bir sağlık sigortası platformu geliştirdiğinizi düşünün. Web portalı var, mobil uygulama var, bir de anlaşmalı hastanelerin entegre olduğu B2B Partner API’si var. Üç farklı client, üç farklı dünya.

Web ekibi diyor ki: “Poliçe detay sayfasında tüm verileri tek seferde çekmek istiyoruz, REST gayet iyi.”

Mobile ekip diyor ki: “Abi biz 3G’de çalışan kullanıcılarımız var, payload’ı 10KB’ın altına çekmemiz lazım, GraphQL’e geçelim.”

B2B Partner ekibi diyor ki: “Hastane sistemleri legacy, XML bile kabul ederler, yeter ki stabil olsun.”

Backend ekibi olarak ne yapıyorsunuz? Herkesi memnun edecek ortak bir API tasarlıyorsunuz. Sonuç? Kimse memnun değil. Web ekibi fazla endpoint’ten şikayetçi, mobile ekip over-fetching’den şikayetçi, B2B ekibi “neden her release’de bir şeyler bozuluyor” diye soruyor.

İşte BFF pattern’i tam da bu noktada devreye giriyor. Ama önce şu meşhur karışıklığı çözelim.

BFF vs API Gateway: Şu Karışıklığa Son Verelim

Bu iki kavramı karıştırmak, microservices dünyasının en yaygın yanılgılarından biri. Hatta bazı ekiplerin “API Gateway’imiz var, BFF’e gerek yok” dediğini duyuyorum. Hayır, ikisi aynı şey değil. Complementary yani birbirini tamamlayan pattern’ler.

API Gateway altyapı odaklıdır: Request routing, Authentication/Authorization enforcement, Rate limiting, SSL termination, Load balancing.

BFF ise deneyim odaklıdır: Birden fazla microservice’ten veri aggregation, Client’a özel response transformation, Client’a özel orchestration.

“A BFF is tightly coupled to a specific user experience, and will typically be maintained by the same team as the user interface.” — Sam Newman

Yani Gateway tüm client’lara tek bir kapıdan hizmet verirken, BFF her client tipine özel bir backend sağlıyor. Production’da mimari şöyle görünüyor: Client API Gateway BFF Microservices

BFF Olmadan Yaşadığım Acılar

Sağlık sigortası platformunda BFF olmadan geliştirme yaptığımız dönemleri hatırlıyorum. Her gün yeni bir problem.

1. Over-fetching Cehennemi

Mobile uygulamada sigortalının özet bilgilerini gösteren bir kart var. Sadece ad, soyad, poliçe numarası ve son provizyon tarihi lazım. Ama Policy Service’ten gelen response’ta 50+ field geliyor, 4 tanesi lazım. Mobile kullanıcısı 3G’de bu payload’ı indiriyor. Pil gidiyor, kota gidiyor, kullanıcı mutsuz.

2. Under-fetching ve N+1 Problemi

Tam tersi durum da var. Sigortalının provizyon geçmişi sayfasını düşünün. Tek bir ekranda şunlar lazım:

  • Sigortalı bilgileri (Policy Service)
  • Son 10 provizyon (Claim Service)
  • Her provizyonun hastane bilgisi (Provider Service)
  • Her provizyonun onay durumu (Approval Service)

Sonuç: 1 + 1 + 10 + 10 = 22 HTTP request! Mobile’da bu sayfayı açmak 4-5 saniye sürüyor. Kullanıcı uygulamayı sildi bile.

3. “Backend Ekibini İkna Et” Döngüsü

“Her frontend ekibinin bir değişikliğe ihtiyacı olduğunda, önce backend ekibini bunun gerçekten gerekli olduğuna ikna etmesi gerekiyordu. Sonra story yazılmalı, önceliklendirilmeli, ele alınmalı, geliştirilmeli ve iletilmeliydi.” — SoundCloud / Thoughtworks

Bizde de aynıydı. Mobile ekip diyor ki “şu endpoint’e bir field daha ekleyin.” Backend ekibi diyor ki “sprint’imiz dolu, 3 hafta sonraya alalım.” Bu arada Web ekibi de aynı endpoint’e farklı bir field istiyor. Backend her iki isteği birleştirmeye çalışıyor, API tasarımı karmaşıklaşıyor, kimse memnun değil.

Ne Zaman BFF Kullanmalı? (Karar Checklist’i)

Her microservices projesine BFF eklemek doğru değil. Bazen gereksiz kompleksite eklemiş olursunuz, bazen de “keşke en baştan eklenseydik” dersiniz. Yıllar içinde oluşturduğum bir karar matrisi var.

BFF Kullanmalısın ✓

  • 3+ farklı client tipin var (web, mobile, partner, IoT)
  • 3+ microservice’ten aggregation gerekiyor
  • Frontend ve backend ayrı ekipler
  • Mobile için optimize payload şart
  • Legacy backend’i modernize ediyorsun (Strangler Pattern)
  • Client’lar farklı hızda evolve ediyor

BFF Kullanma ✗

  • Tek client tipin var
  • Basit CRUD operasyonları
  • BFF sadece passthrough yapacak
  • DevOps kapasiten yetersiz
  • Tüm client’lar aynı veriyi istiyor

“Bir mobil UI veya üçüncü taraf için spesifik fonksiyonellik sağlamanız gerektiği an, en başından her taraf için BFF kullanmayı ciddi olarak düşünürüm.” — Sam Newman

Pratik Skor Kartı

Şu soruları cevaplayın, her “Evet” 1 puan:

  • Birden fazla client tipiniz var mı? (web, mobile, partner)
  • Client’lar arasında UX farkı belirgin mi?
  • Tek bir ekran için 3+ microservice’e istek atmanız gerekiyor mu?
  • Frontend ekibi, backend değişikliği için ortalama 1 haftadan fazla mı bekliyor?
  • Mobile payload size’ı kritik bir metrik mi?
  • Client’lar farklı release cycle’larda mı?
  • Farklı client’lar için farklı authentication flow’ları var mı?
  • Backend ekibi “hangi client için” diye sormak zorunda mı kalıyor?

Sonuç:

  • 6+ puan: BFF kesinlikle değerlendirilmeli
  • 3-5 puan: Duruma göre, pilot ile başla
  • 0-2 puan: Muhtemelen gereksiz, GraphQL veya API Gateway yeterli

BFF’i Yanlış İmplemente Etme Yolları (Anti-Patterns)

BFF pattern’ini doğru anlayıp yanlış implemente eden çok ekip gördüm. Hatta kendim de bu hataları yaptım. İşte en sık karşılaştığım anti-pattern’ler:

1. God BFF (Tanrı BFF)

En tehlikeli anti-pattern. BFF zamanla her şeyi yapan bir canavara dönüşüyor.

Belirtiler:

  • BFF içinde business rule’lar var (indirim hesaplama, prim calculation)
  • BFF’in kendi database’i var
  • BFF için ayrı bir ekip kurulmuş
  • Bir değişiklik için impact analysis gerekiyor
  • BFF’in unit test’leri backend service’lerden daha fazla

Kural: BFF’te if-else ile business decision alıyorsanız, muhtemelen yanlış yoldasınız.

2. Business Logic Sızması

God BFF’in küçük kardeşi. Tam bir God BFF değil ama business logic yavaş yavaş sızıyor.

Belirtiler:

  • “Şimdilik burada kalsın, sonra taşırız” yorumları
  • Aynı hesaplama birden fazla BFF’te duplicate
  • Backend service’ler “dumb data store” gibi davranıyor
  • BFF’teki bug’lar business impact yaratıyor

3. Shared BFF (Paylaşımlı BFF)

Farklı client tipleri aynı BFF’i kullanıyor. “Nasılsa benzer işler yapıyorlar” diye düşünülüyor.

Belirtiler:

  • BFF içinde if (clientType == “mobile”) blokları
  • Endpoint’lerde ?platform=ios gibi query parameter’lar
  • Bir client için yapılan değişiklik diğerini etkiliyor
  • Release’ler koordinasyon gerektiriyor

“Geriye bakınca, platform başına bir BFF sağlamak daha faydalı olurdu, çünkü Android ve iOS uygulamaları farklı API ihtiyaçlarını ortaya çıkaracak kadar birbirinden farklı.” — SoundCloud Retrospective

4. BFF Sprawl (BFF Çoğalması)

Shared BFF’in tam tersi. Her feature için ayrı BFF açılıyor.

Belirtiler:

  • 10+ BFF var ama 3 client tipi
  • BFF’ler arası kod duplicasyonu çok yüksek
  • Operational overhead artıyor (monitoring, deployment, on-call)
  • Hangi BFF’in ne iş yaptığı belirsizleşiyor

Kural: “One experience, one BFF” — Client tipi başına bir BFF, feature başına değil.

5. BFF’i Performans Bottleneck’i Yapmak

BFF, client ile backend arasına bir hop daha ekliyor. Bu hop’u yönetemezseniz, latency killer olur.

Belirtiler:

  • BFF response time’ları backend’den yüksek
  • Downstream service’lere sequential call yapılıyor
  • Circuit breaker yok, bir service fail olunca her şey fail
  • Caching stratejisi yok

Spring Boot ile Production-Ready BFF

Teoriden pratiğe geçelim. Sağlık sigortası platformu için Mobile BFF yazıyoruz. Sigortalının dashboard ekranını düşünün: poliçe bilgisi, son provizyonlar, bakiye ve yakındaki anlaşmalı hastaneler tek ekranda gösterilecek.

Parallel Aggregation ile Dashboard Service

Sequential call yapmak yerine Mono.zip() ile parallel çağrı yapıyoruz:

public Mono<MobileDashboardResponse> getDashboard(String odNo, Double lat, Double lon) {

return Mono.zip(

policyClient.getActivePolicy(odNo),

        claimClient.getRecentClaims(odNo, 5),

        balanceClient.getBalance(odNo),

        providerClient.getNearbyProviders(lat, lon, 10)

    ).map(tuple -> mapper.toDashboardResponse(

        tuple.getT1(), tuple.getT2(), tuple.getT3(), tuple.getT4()

    ));

}

Bu sayede 4 servis çağrısı paralel çalışıyor. Sequential’da 400ms süren işlem ~150ms’e düşüyor.

Graceful Degradation

Kritik nokta şu: Yakındaki hastaneler servisi çökerse, tüm dashboard mı fail olmalı? Hayır! Kullanıcı en azından poliçe bilgisini görmeli.

// Non-kritik servisler için fallback

Mono<List<Provider>> providersMono = providerClient

    .getNearbyProviders(lat, lon, 10)

    .onErrorReturn(Collections.emptyList())   // Hata olursa boş liste

    .timeout(Duration.ofSeconds(2))           // Ekstra timeout

    .onErrorReturn(Collections.emptyList());

Resilience4j Circuit Breaker

Downstream service sürekli fail oluyorsa, boşuna istek atmayı kes:

@CircuitBreaker(name = “claimService”, fallbackMethod = “getRecentClaimsFallback”)

public Mono<List<Claim>> getRecentClaims(String odNo, int limit) {

    return claimServiceClient.get()

        .uri(“/api/claims?odNo={odNo}&limit={limit}”, odNo, limit)

        .retrieve()

        .bodyToFlux(Claim.class)

        .collectList();

}

BFF Ownership: Kim Sahiplenmeli?

Bu konu teknik olmaktan çok organizasyonel. Ve inanın, doğru ownership modeli seçmek, doğru teknoloji seçmekten daha kritik.

Conway’s Law Hatırlatması

“Sistemleri tasarlayan organizasyonlar, kendi iletişim yapılarının kopyası olan tasarımlar üretmeye mahkumdur.” — Melvin Conway, 1967

Bu ne demek? Eğer frontend ve backend ayrı ekiplerse ve BFF’i backend ekibi sahipleniyorsa, frontend ekibi yine aynı “backend’i ikna et” döngüsüne girecek. BFF’in tüm amacı boşa gidecek.

Önerim: Frontend Ekibi Sahiplensin

“BFF, spesifik bir kullanıcı deneyimine sıkı sıkıya bağlıdır ve tipik olarak kullanıcı arayüzüyle aynı ekip tarafından maintain edilir.” — Sam Newman

Ama frontend ekibinin backend bilgisi yoksa? SoundCloud’un çözümü çok akıllıca:

  • Platform ekibi: BFF framework, shared library, monitoring altyapısı sağlar
  • Frontend ekibi: Bu framework üzerine kendi BFF’ini yazar ve sahiplenir
  • Backend ekibi: Domain service’leri sağlar, BFF’e karışmaz

Ve….

Uzun bir makale oldu ama BFF pattern’i gerçekten bu kadar nüanslı bir konu. Son olarak 3 temel prensibi özetleyeyim:

1. Ownership Her Şeyi Belirler

Kullanıcı deneyimini inşa eden ekip, BFF’i de sahiplenmeli. Platform ekibi framework sağlar, bireysel BFF’leri değil. Bu, Conway’s Law ile uyumlu tek model.

2. Sınırlar Şişmeyi Önler

Business logic domain service’lerde kalmalı. BFF sadece orchestration, aggregation ve transformation yapmalı. God BFF anti-pattern’ine düştüğünüzü anlamanın en kolay yolu: BFF’te if-else ile iş kuralı yazıyorsanız, yanlış yoldasınız.

3. Teknoloji Organizasyonu Takip Eder

GraphQL Federation mı, REST BFF mi, yoksa API Gateway mı kullanacağınız sorusu, aslında “ekiplerimiz nasıl organize?” sorusunun cevabına bağlı. Mimari pattern, organizasyonel hedeflere hizmet etmeli — tersi değil.

Ve son bir söz: BFF aslında bir mimari pattern değil, organizasyonel bir karar. Teknik implementasyon kolay kısım. Zor olan, ekiplerin autonomy’sini sağlayacak yapıyı kurmak.

Bir sonraki makalede görüşmek üzere, sağlıcakla kalın (:

Kaynaklar

  • Sam Newman — “Backends For Frontends” (samnewman.io)
  • Thoughtworks — “BFF @ SoundCloud”
  • Martin Fowler — Microservices Patterns
  • AKF Partners — “Backend for Frontend Pattern: Dos and Don’ts”