BFF ile Ön Yüz Uygulamalarınız için Veri Modelinizi Optimize Edin

Oğuz Kılıç
14 min readMay 21, 2020
Author: Kasper Rasmussen

English: https://medium.com/@oguzkilic/optimize-your-data-model-for-your-frontend-applications-with-bff-2195aec6e6cf

Modern web dünyasında kullanıcı deneyimi ve buna bağlı olarak geliştirilen uygulamaların ihtiyaçları her geçen gün bizi bir sonraki adımın ne olacağını düşünmeye itiyor. Kabul edelim ki bu artık hepimiz için olağan bir durum.

Her gün kullanıcılarımızın ihtiyaçları değişiyor, yeni teknolojiler ve kütüphaneler odak noktamıza yerleşiyor.

Frontend (Ön Yüz), her ne kadar ilk duyduğunuzda “Web” ve buna bağlı olarak “Html, Css, JavaScript” ile ilişkilendirilmiş olsa da dijital dünyada gördüğünüz kullanıcı arabirimine sahip tüm cihazlarda ekrandan size yönelen her şey aslında ön yüz kapsamına giriyor. Bu şekilde düşündüğünüzde web uygulamalarının size dokunan ve sizinle etkileşime geçen ara katmanına rahatlıkla ön yüz diyebiliriz.

Geliştirdiğimiz ön yüz uygulamaları bir veri kaynağından beslenerek son kullanıcıyla bir dizi etkileşime girerler. Bu veri kaynağını çoğunlukla API olarak tanımlarız. Ancak, bu veri kaynakları genellikle web, mobil gibi farklı ön yüzlere uygun yazılmadığı için bunları General Purpose API olarak düşünebiliriz. Web’e uygun yazdığınızda mobil uygulamalarınızın farklı veri ihtiyaçlarına yanıt vermekte zorlanırsınız veya tam tersi durum yaşanır. Hepsini desteklemeye çalıştığınızda ise karmaşıklık boyutu artar.

Bu konuda ortak bir çözüm bulmak neredeyse imkansızdır. Bir istemciye uygun yazılan servis diğer istemciler için ya yeniden yazılır ya da yeni istemciye uygun hale getirilecek bir uç nokta daha yaratılır. Karmaşık bir ürün için birden çok istemciye veri sunmaya çalışıyorsanız ne demek istediğimi anladığınızı düşünüyorum.

Klasik anlamda bir web uygulamasının ve mobil uygulamanın veri ihtiyacı farklıdır. Çünkü mobil bant genişlikleri yıllardır daha ufak istek/yanıt modeli ile çalışmamız gerektiğini gösteriyor. Yani bir web uygulamasına gönderdiğiniz veri modelini olduğu gibi mobil istemcinize gönderemezsiniz. Yine aynı şekilde yapılacak çok sayıda network işlemi mobil cihazınızın pil süresine doğrudan etki edebilen konulardan yalnızca bir tanesi.

Neden API benim ihtiyacıma uygun dinamik bir veri modeli sunmasın?

Farklı kullanıcı arayüzleri ve yaşanan bu farklı deneyim, veri modelinin de farklılaşması gerektiğini gösteriyor. Genel amaca uygun olarak geliştirilen API’lar bu durumda bizim için kısmen yetersiz hale geliyor. Yeni bir özellik geliştirdiğinizde genel amaca uygun olarak geliştirilen API ile web, mobil ve diğer ön yüzler için ayrı ayrı entegrasyon yapmamız ve veri modelini ilgili istemciye uygun hale dönüştürmek amacıyla bir dizi işlemden geçirmemiz gerekiyor. Bu durum genellikle geliştirici takımları için ekstra efor ve zaman maliyeti olarak karşımıza çıkıyor.

Yazının buraya kadar olan kısmı gerçek dünyada sıkça karşılaştığımız bir dizi problemi açıklığa kavuşturmaktı. Bundan sonrası için çözümler ve çözüm için uygulanabilecek bazı yöntemlerden bahsetmek istiyorum.

Backend for Frontend

BFF olarak tanıdığımız bu yaklaşım aslında çok yeni sayılmaz. Zaten var olan bir dizi yaklaşımın yeni bir şekilde tanımlanmasıyla beraber popülaritesi artmış ve yaşanan gerçek dünya problemlerine ideale yakın bir çözüm sunmayı da vaat etmiştir.

BFF yaklaşımının özetle SoundCloud’da yaşanan bir dizi probleme aranan çözüm sonrası ortaya çıktığını söyleyebiliriz. Burada belirtilen yaklaşım genel amaçlı API yerine ürünün sahip olduğu deneyimlere göre ona özel olarak geliştirilmiş API’lar oluşturulmasını savunuyor.

Bu yaklaşım aslında bize ön yüzün ihtiyacına uygun veri modelini sunan bir katman daha oluşturmamız gerektiğini gösteriyor. Aynı zamanda BFF adı verilen katmanın ön yüzü bilen kişiler tarafından yazılması gerekiyor ve yine ön yüz ekipleri tarafından sahiplenilmesi gerekiyor. Sanırım neye ihtiyacınız olduğunu sizden daha iyi kimse bilemezdi doğru değil mi?

BFF Nedir?

Backend for Frontend yaklaşımı ön yüzün ihtiyaçlarına yönelik özelleştirilmiş servisler olarak tanımlanmaktadır.

Phill Calcado, bu yaklaşım için öncelikle Hollanda’ca BEFFE tanımını önermiş ancak ekip bu ismi reddetmiş. Ardından ekibin teknik lideri Nick Fisher’ın önerisiyle BFF olarak tanımlanmış.

Yaklaşımın amacı ön yüze en uygun veri modelini sunmak ve karmaşıklığı azaltmaktır. BFF yaklaşımı çeşitliliği artan istemcilerde ortaya çıkan farklı kullanıcı deneyimlerini ele almayı kolaylaştırmayı amaçlamaktadır. Belirgin bir kullanıcı deneyimine sıkı sıkıya bağlılığın hedeflendiği bu yaklaşım ile API’ların arayüze uyarlanması kolaylaştırılmaktadır.

Yaklaşımın temel prensibi üzerinden yola çıktığımızda her arayüz için farklı bir BFF oluşturulması gerektiğini söyleyebiliriz.

Aggregation Layer

Mikroservis dünyasında ürününüze ait birçok alanı yönettiğiniz sayısız servislere sahip olabilirsiniz. Bu durumda bir ürünün bilgilerini toplamak için çok sayıda istek yapmanız kaçınılmaz. Aggregator adı verilen servisler ile daha alt seviyedeki mikroservis’lerden bir ürüne ait bilgileri toplayarak tek bir yanıt ile dönmemiz mümkün.

Aggregate is a pattern in Domain-Driven Design. A DDD aggregate is a cluster of domain objects that can be treated as a single unit. An example may be an order and its line-items, these will be separate objects, but it’s useful to treat the order (together with its line items) as a single aggregate. — Martin Fowler

Ancak, aggregation katmanı farklı görevler de üstlenebilir. Örneğin, bir siparişin tamamlanması için gereken kararları bu katmanda verebilir ve sadece siparişi tamamlama görevi olan mikroservis’e bu işlemi yaptırabilir. Bu gibi mantıksal operasyonlar yani aslında business case’lerin tutulması için uygun bir katmandır.

Foundation Layer

Foundation, aggregator’lara ihtiyacı olan verileri dönen daha alt mikroservis’lerin olduğu katmanı ifade ediyor. Gerçek mikroservis’ler olarak nitelendirebiliriz. Doğrudan DB ile konuşan ve kendisine ne yapılması gerektiği söyleniyorsa onu doğrudan yapan alt seviye servisler diyebiliriz. Aggregation ayrımının güzelliği istediğinizde alt katmandaki mikroservis’lerinizi business logic’e dokunmadan yeniden yazabilir veya arkasındaki DB modelini değiştirebilirsiniz.

Ancak, her durumda bu pattern gerekli olmayabilir. Bu ayrımları ihtiyaca uygun şekilde tasarlamak gerekiyor.

Burada şöyle bir soru sorabilirsiniz: BFF katmanı tüm bunların ortasında tam olarak nerede konumlandırılmalı?

BFF katmanı Aggregation katmanın hemen önünde olmalı. Aggregator’ların Foundation’lardan toplayıp tek yanıtta döndüğü verileri BFF katmanı devralarak ön yüze uygun hale getirmeli. Ancak, her zaman BFF’ler aggregation katmanıyla konuşmak zorunda değil.

Bazen doğrudan alt seviye mikroservis’ler ile konuşabilirsiniz. O nedenle, bu konuyu, genel olarak aggregation önünde olmalı ancak ihtiyaca göre her yerde olabilir şeklinde özetleyebiliriz.

service layers

Burada şöyle düşünebilirsiniz neden üçüncü katmanı oluşturmam gerekiyor? Aggregation bu işi yapamaz mı?

İlk bakışta aggregation katmanı BFF’in yerini alabilir gibi görünse de tam olarak gerçek dünyada böyle olmuyor. Zamanla buranın fazla büyümesi, bakım maliyeti gibi nedenlerden dolayı BFF üçüncü katman olarak konumlandırılabilir.

Elbette client’lara ulaştırılacak veri yoluna yerleştirilen her katman bir miktar latency anlamına geliyor. Bazı çözümler beraberinde mutlaka bir bedel ile gelir.

BFF Katmanını GraphQL ile Güçlendirebiliriz

BFF katmanı ile ön yüzde kullanılacak component yapılarımıza uygun ortak bir veri modeli sunabiliriz.

Örneğin bir 3rd party recommendation servisinden kullanıcıların ilgisini çekebilecek ürünleri alarak ön tarafta göstermek istiyorsunuz. Bunun için elinizde React ile geliştirdiğiniz bir ürün component’ı var diyelim.

Ürün component’ı kendisine verilen prop’lar ile şekilleniyor. Ancak, farklı servisler tutarsız ürün bilgileri dönebilir. Örneğin, fiyat bilgisini ürün component’ımız $1.156,09 şeklinde göstermeli. 3rd party servis genel amaca uygun hazırlandığı için bize 1156.9 formatında gönderiyor bu bilgiyi.

Başka bir örnekte ise kendi ürün servisimiz farklı bir alana ait ürün bilgisi için fiyatı 1.156.9 olarak gönderiyor. Veya bir servis fiyat bilgisini String olarak dönerken diğeri Int dönebilir. Bu örnekleri çoğaltmak mümkün.

Biz ön yüzde tek bir ürün component’ı kullanmak istiyoruz ve verinin formatının standart olmasını istiyoruz. Bu nedenle, ön yüzde minimum efor harcanacak, uygun bir formata dönüştürmeliyiz.

After Bff transformation

Tüm bu farklılıkları ön yüzde desteklemeye çalışmak gerçeklikten oldukça uzak. Client’ı aptal yapıda bırakmanın birçok avantajı var. Her yanıtı UI tarafında işlemeye çalışırsanız okunabilirliğini yitirmiş, bakımı zor, devamlı bug üreten bir UI’a sahip olursunuz. Aynı zamanda bu component’ın test edilebilirliğinin zorlaşması anlamına gelir.

Program to an ‘interface’, not an ‘implementation’.

Design Patterns: Elements of Reusable Object-Oriented Software (1994)

BFF katmanı daha çok ön yüz geliştiricilerin sahiplenmesi gereken bir alan demiştik. Bu noktada kullanılacak teknoloji ve dil de oldukça önemli. Node.js üzerinden ilerlemek burada aktif kod yazacak kişilerin ön yüz geliştiriciler olduğunu düşündüğünüzde daha cazip gelecektir muhtemelen. Çünkü, yapılan tam olarak Input — Output. Ancak, BFF’lerin tek bir dil veya teknoloji ile yazılması gerektiği ile ilgili katı kurallar koymak da bir o kadar yanlış.

Bahsettiğimiz BFF yaklaşımının çözmeyi hedeflediği sorunlara baktığımızda bu katman için GraphQL kullanmanın bizi ideal çözüme daha fazla yaklaştıracağını düşünüyorum.

GraphQL sayesinde BFF katmanında çok daha fazla esneklik kazanmamız mümkün. İhtiyacımız olmayan verileri istemediğimizde tek yapmamız gereken gönderdiğimiz GraphQL sorgusunda o verileri istememek. Böylece mobil için ve web için ayrı BFF katmanları oluşturma fikri yerini GraphQL ile birlikte tüm istemciler için deneyime özel, bağımsız Bff yaklaşımına bırakıyor.

Phil Calcado BFF ve GraphQL ilişkisiyle ile ilgili konuyu şöyle özetliyor;

Were GraphQL available back then and had we decided to use it, things would be quite different. When we use GraphQL, we don’t necessarily need a Presentation Model at all, and if we do use one, it can be implemented on the client application, as GraphQL makes it possible to get all data needed in a single request.

Ön yüz tarafında bir modülün veya sayfanın veri ihtiyacını karşılamak için birden fazla BFF servisine istek yapmak maliyetli bir işlem. Aynı zamanda bu servisler için her istemcide ayrı ayrı implementasyon yapmak başka bir maliyet nedeni. Belki monolitik bir web uygulaması yerine micro frontend veya bağımsız parçalardan oluşan bir web mimarisi tasarlamak istiyor olabilirsiniz. Tüm istemcilerin sadece bir tane endpoint’i bilmesi işleri oldukça sadeleştirebilir.

Bu noktada ihtiyacımız netleşti sanırım. Evet, bir gateway çözümüne ihtiyacımız var.

Gateway Nedir?

Gateway, client’ların servislere doğrudan erişmek yerine güvenlik ve yetkilendirme gibi kontrollerin yapıldığı bir API üzerinden erişme yöntemidir. Gateway’ler yapılan tek bir istek sonucunda dağıtık biçimdeki farklı servislerden yanıtları toplayıp, birleştirilerek tekrar geri dönerler.

Tek bir UI ekranı oluşturmak için birden çok servisle iletişim kurmak istek sayısının artmasına ve UI tarafında bir gecikmeye neden olur. İdeal dünyada client’ın yapacağı birden fazla isteğin Gateway aracılığı ile sunucuda yapılması ve verimli bir şekilde toplanması gerekmektedir.

Dolayısıyla, GraphQL üzerinde geliştirdiğimiz tüm BFF’ler ile bir gateway üzerinden haberleşmemiz gerekiyor. Aslında arkada hangi BFF’e gidildiği ön yüz için çok önemli değil. Bizim tek ihtiyacımız ürün bilgilerini component modelimize uygun formatta en basit ve en hızlı şekilde aktarmak.

Kaynak

Apollo Federation

Apollo, GraphQL için bir client çözümü olarak ortaya çıkmıştı. Ancak, farklı yaklaşımı ile kısa sürede GraphQL ekosistemi için önemli bir oyuncu haline geldi. Apollo’nun server ve client çözümleri tam bir uyumluluk içinde çalışıyor. Modern frontend dünyasının hızla ilerlediği bir yolda kullandığımız parçaların birbirleri ile olan uyumluluğu bizim için oldukça önemli.

Apollo Federation’ı (@apollo/federation) GraphQL şemalarınızı birleştirilebilir hale getirmek için BFF servislerimizde kullanabiliriz.

bff gateway model-1

Apollo-Server şimdilik sadece Node.js üzerinde çalışıyor ve Express, Hapi, Koa gibi framework’ler ile entegre çalışabiliyor. Dolayısıyla, federation gibi yapılar için tüm BFF’lerinizde bu alt yapı ile ilerlemeniz gerekiyor. Eğer ben bir BFF servisimde Go kullanmak istiyorum diğerinde Node.js derseniz schema stitching gibi bazı konularda ekstra efor harcamanız gerekecektir.

Apollo Gateway

Gateway (@apollo/gateway) tanımladığımız servis listesindeki BFF’lere bağlanarak şemalarını alıyor ve bu şemaları tek bir grafik üzerinde compose ediyor. Böylece, Query’lerimizi gateway’e aktarıyoruz. Gateway, compose edilen şemaya bakarak hangi servise gitmek istediğimizi anlıyor ve Query’mizi ilgili BFF’e yönlendiriyor. BFF’den dönen cevabı doğrudan bize iletiyor.

bff gateway model-2

Aynı anda birden fazla Query’i gateway’e gönderebiliyor ve bu istekleri paralel olarak ilgili BFF servislerine yönlendirebiliyoruz. Tek bir Uç nokta, tek bir Sorgu ve tek bir Yanıt. Kulağa oldukça iyi geliyor.

Apollo-Server üzerinde Federated Schema ve Gateway çözümünü birlikte kullanarak ideal bir BFF katmanı yaratabilirsiniz.

Ara Katmanlarda Nelere Dikkat Edilmeli?

Ara katmanlar için ilk düşüncem oldukça sıkı denetlenmesi gereken yerler olduğu yönünde. Client’a ulaşan veride bir problem olduğunda bunun hangi katmandan kaynaklandığını tespit edebiliyor olmamız gerekiyor.

Schema Updating:

Eğer Federated Schema’lar ile Apollo Gateway üzerinden bir yönlendirme yapıyorsanız dikkat etmeniz gereken ilk konu ise deployment aşamasıdır.

gateway service list example

Apollo-Gateway sadece ilk ayağa kalktığında servis listesindeki BFF’lerin şemalarını alır.

gateway schema loading

Eğer ürün bilgilerini getiren BFF servisinizde şema üzerinde bir değişiklik veya ekleme yaptıysanız bunun gateway’e yansıması için /schema-reload gibi bir endpoint yaratabilir ve buraya bir GET isteği geldiğinde gateway’in startPollingServices metodunu çağırabilirsiniz. Bu sayede servislerin güncel şemaları gateway’e alınacak ve tekrar compose edilerek güncellenmiş olacak. PoolingServices işini bitirdiğinde stop edebilirsiniz.

Bu işlemi Continuous Delivery yaparken otomatize edebilirsiniz. Örneğin, Spinnaker gibi bir platform kullanıyorsanız bir deployment hook’u ile gateway’in /schema-reload endpoint’ine bir kez GET isteği yaparak şemaların güncellenmesini sağlayabilirsiniz.

Test:

Uyguladığımız BFF modelinde servislerimizin test coverage’larını %100 olarak belirlemek çok ütopik sayılmaz. Unit ve Integration testler yazarak bu katmanın doğru sonuçlar döndüğünden ve sorunsuz çalıştığından emin olmak bizim için oldukça kritik. Genellikle test coverage’ları için %100 ifadesi kullanmak ideal dünya için çok gerçekçi olmayabilir. Ancak, söz konusu veri manipülasyonu yaptığınız bir ara katman ise bunu yapmaya bir anlamda mecbursunuz. En azından geceleri rahat uyuyabilmek için.

Metrikleri Toplamak

BFF katmanınıza ait metrikleri toplamak ve bunları takip edecek dashboard’lar hazırlamak servisinizin davranışlarını anlık olarak takip edebilmenize yarar. Beklentiniz dışında bir durum yaşandığında bundan anlık olarak haberdar olmanız için kritik bir konudur. Fakat, söz konusu GraphQL olduğunda metriklerin on-premise çözümler ile nasıl entegre edilebileceği konusunda kaynak sayısı oldukça az.

Apollo çözümlerine baktığımızda apollo-graph-manager bu metrikleri oldukça güzel bir şekilde toplayarak cloud’a gönderebiliyor. Diğer taraftan, ücretli bir servis olması ve cloud üzerinde çalışıyor olması bazı şirketlerin güvenlik politikaları dikkate alındığında riskli bir durum yaratabilir.

Metrikler üzerinde daha fazla hakimiyet sağlamak adına on-premise çözümlere yönelmek isteyebilirsiniz. Bununla beraber, graph-manager tamamen GraphQL’e özel bir araç olduğu için çok faydalı ve güçlü bazı özellikler barındırıyor.

Apollo, GraphQL metrikleri için bir tracing yöntemi sunuyor bize. Eğer metrikleri graphql-manager’a göndermeyecekseniz muhtemelen bir metrik database’i kullanmak isteyeceksiniz ve Prometheus gibi çözümlere yöneleceksiniz. Bu durumda Apollo’nun sunduğu lifecycle metotları ile elde edeceğiniz verileri Prometheus’a uygun formata dönüştürmeniz gerekmektedir. Bu işlemi yapacak ufak bir apollo plugin’i yazarak export ettiğiniz verileri /metrics gibi bir endpoint üzerinden Prometheus’a sunabilirsiniz.

GraphQL’in yapısı gereği doğru metrikleri toplamamız gerekiyor. Sadece request/response sürelerini içeren metrikler toplamak yoğun trafik altında yavaşlıkk yaşandığında bunun tam olarak nereden kaynaklandığını tespit etmeniz için yeterli olmayacaktır.

export metrics — custom apollo plugin

Örneğin, bir Query attığınızda bu Query parse edilirken geçen süreyi bilmemiz faydalı olacaktır. Yine aynı şekilde Query’nin resolve olma süresi, resolver’ın ne kadar sürede işini tamamladığı gibi operasyonları sürekli olarak bilmemiz gerekiyor. Bu sayede Query’nizdeki hangi alanda problem yaşanıyor ve optimize edilmeli gibi durumları yakalama şansınız var. (parsing, validation, resolve, execution, request time gibi metrikler oluşturabilirsiniz.)

Metrikleri Görselleştirmek

Prometheus’a uygun formatta export ettiğimiz metrikleri Grafana ile görselleştirebiliriz. Eğer kubernetes gibi bir container orchestrator’ü kullanıyorsanız service monitörlerinin ayarlarını yaptığınızda container’da çalışan BFF’lerinizin CPU, Ram, Heap gibi bilgilerini de toplayarak yine Prometheus’a gönderebilir ve Grafana üzerinde bunları görselleştirebilirsiniz.

gateway query responded metrics

Error Tracking

BFF gibi ara katmanlarda yaşanabilecek hataları anında track edebiliyor olmamız gerekiyor. Burada Apollo bize hataları yakalayabileceğimiz bir lifecycle sunuyor. Ufak bir Apollo-Sentry plugin’i yazarak fırlatılan exception’ları yakalayıp Sentry gibi güçlü bir tracking aracı ile loglayabilir ve gerekli uyarı mekanizmalarını (slack, mail vb) aktif hale getirebiliriz. Yakaladığınız exception’ları daha rahat sorgulamak isterseniz beraberinde Splunk da kullanabilirsiniz.

gateway configuration

Sentry, Node.js ve client üzerindeki JavaScript hatalarını track etmek ve loglamak için ideal bir çözüm sunmaktadır.

Log

BFF servislerimizin anlamlı loglar üretmesi sağlıklı çalışıp çalışmadığı ile ilgili varsayımda bulunabilmek adına yine önemli bir konu. Loglarımızı uygun bir formatta stdout’a basabiliriz. Yine Kubernetes gibi bir orchestrator üzerinde çalışıyorsanız bu logları stdout’dan toplayarak Splunk gibi bir araca gönderebilirsiniz ve alert mekanizmaları kurabilirsiniz. Logları toplamak için yine Apollo’nun lifecycle metotlarından faydalanabilirsiniz.

Hatalara karşı dayanıklılık

Veri kaynağı olarak kullandığımız aggregation’lar bazen hiç yanıt veremez veya çok geç yanıtlar verebilir. Bu durumdan BFF’lerin haberdar olması ve servis isteğini kesmesi gerekmektedir.

Örnek Senaryo:

React uygulamamızdan BFF servisimize bir GraphQL sorgusu göndererek sayfamızdaki favori ürünler modülüne ait bilgileri almak istiyoruz. BFF üzerinden ilgili servis çağrısını aggregation’a yönlendirdik. Fakat, aggregation çok geç yanıt vermeye başladı. Muhtemelen birtakım sorunlar yaşanıyor. Bu durumda BFF cevabın gelmesini beklemeye başladı.

content placeholders — loading state

Ön yüzde BFF’den gelen loading state’inde kaldınız ve çok muhtemel ki bu bilgiyle kullanıcılarınıza placeholder gösteriyorsunuz. Placeholder servis yanıt verene kadar kullanıcıya gösterilecek ve kötü bir kullanıcı deneyimi yaşanacaktır. Placeholder’lar genel olarak kullanışlıdır ancak son kullanıcıya gösterdiğiniz süre uzadığında sitenizin yavaş olduğu ile ilgili bir algı yaratılmasına hizmet eder.

Circuit Breaker

Circuit Breaker Design

Bu gibi durumları yönetebilmek adına Circuit Breaker pattern’ını uygulamanız gerekecektir. Belirlediğiniz timeout süreleri aşıldığında çok hızlı bir şekilde BFF servisimiz client’lara fallback durumunu dönmeli. Client bu bilgiye göre ilgili alanı veri gelene kadar göstermemeli veya fallback senaryosuna göre farklı bir aksiyona geçmeli.

Bu sayede gereksiz kaynak tüketimini de optimize edebiliriz. Node.js için Netflix’in Hystrix ya da Red Hat’in Opposum paketleri gibi alternatif çözümleri kullanabilirsiniz.

Circuit Braker pattern’ında üç temel noktamız bulunuyor.

  • Open durumunda belirlediğiniz reset timeout süresi boyunca başarısız olan istekler kesilir.
  • Closed durumunda istekler kesintisiz serbest bırakılır çünkü herhangi bir başarısız istek yoktur.
  • Half-Open durumunda hala problemin devam edip etmediğini anlamak adına bazı isteklere geçiş izni verilir, eğer problem devam etmiyorsa Closed durumuna geçilir, devam ediyorsa Open durumunda kalınır.

Retry Mekanizması

Yapılan bir istek başarısız olduğunda bazen tekrar denemeniz gerekebilir. Retry mekanizması bir isteğin belirlenen kurallar bağlamında tekrar denenmesi olarak özetlenebilir.

Örneğin, bir ürün sayfasındaki “sepeti göster” butonuna bastınız ve herhangi aksiyon gerçekleşmedi. Muhtemelen sepet servisinde bir sorun yok ve servisle iletişim kurarken ağ hatasına neden olan bir durum yaşandı. Tekrar denediğinizde sepetteki ürünlerin gösterilme ihtimali var. Böyle bir senaryodaki hataları “Transient Failures” yani Geçici Hatalar olarak tanımlıyoruz.

Bu gibi durumların üstesinden gelebilmek için uygun bir retry mekanizması kurgulanmalı.

Retry için işlemin idempotent olması gerekmektedir. Yani ilgili servisi birden fazla kez çağırdığımızda sonuç aynı ise biz bunu idempotent olarak tanımlıyoruz. Non-idempotent operasyonlar için retry riskli bir işlemdir ve dikkat edilmelidir.

Don’t Repeat Yourself

Eğer büyük bir organizasyona sahipseniz zamanla BFF sayınız artmaya başlayacaktır. Bu durum beraberinde aynı işleve sahip kod tekrarı olan birçok BFF ile sizi baş başa bırakacaktır. BFF mimarinizi kurgularken yeni servisleri yazmayı kolaylaştırmak ve benzer işleri yapan modülleri çekirdek bir kütüphanede toplamak iyi bir başlangıç olabilir. Böylece tüm BFF’lerinizdeki bütünlüğü korurken yeni özellikleri, iyileştirmeleri tek bir noktadan sunabilirsiniz.

Çekirdek bir kütüphane üzerinden inşaa etmediğinizde, BFF servisleri arasındaki tutarsızlık, kod kalitesi ve performans zamanla kaybolmaya başlayabilir.

Örneğin, metriklerinizi toplamak için, hataları yakalamak ve loglamak için her BFF’de tekrar tekrar aynı geliştirmeleri yapmamalısınız. Caching, HealthCheck, Metrics, Service Call gibi işleri çekirdek kütüphaneye bırakabilirsiniz. Bu tür ortak operasyonları çekirdek kütüphanede toplayarak sunmanız karmaşıklığı ve kod tekrarınız azaltmak için iyi bir yol olabilir.

BFF servislerinizin ne kadar büyüyeceği ile ilgili belli başlı sınırlara sahip olmanız gerekmektedir. Aksi taktirde bu ara katmanları yönetmekte zorlanabilirsiniz. Büyük monolitik servislere dönmesini istemezsiniz muhtemelen.

Çekirdek kütüphane oluşturulması tek bir dil ile ilerleyeceğiniz ve bu BFF servislerinizi kütüphaneye bağımlı hale getireceğiniz anlamına gelir. Eğer böyle bir niyetiniz varsa çekirdek kütüphanenizin sınırlarını belirlemeniz ve çok büyümemesine dikkat etmenizde fayda var. Bu katman herhangi bir business logic barındırmayacağı için DRY prensibini kısmen uygulayabiliriz.

Genel Görüş

Son olarak bu yaklaşım takımların sorumluluk alanlarının netleşmesi açısından da önem taşımaktadır.

  • BFF sayınızın kaç tane ile sınırlandırılacağı belirlenmelidir. Feature tabanlı veya ürün/client tabanlı BFF’ler yaratabilirsiniz. Zamanla servislerdeki kod çoğaltmaları artabilir. Bunu nasıl yöneteceğinizi düşünmelisiniz.
  • BFF’ler yalnızca istemciye uygun cevap verebilecek bir katman olmalıdır. Bunun dışındaki davranışları bu işleri yapmakla yükümlü olan alt mikroservis’lere bırakmalısınız.
  • BFF’ler kendi aralarında asla konuşmamalıdır.
  • Yeni BFF oluşturulması ne kadar sürücek? Hızlı ve pratik geliştirme yapabileceğiniz ortama sahip olduğunuzdan emin olmalısınız. Operasyonel maliyetleri minimum düzeyde tutmaya çalışın.
  • Tüm süreçlerinizi otomatize edin, tekrarlardan kaçınmalısınız.
  • BFF’leri ön yüz ekiplerinin sorumluluğuna bırakın veya ön yüzün davranışlarını bilen kişilere bırakın. Geliştirme yapmadan önce mutlaka ön yüzdeki ihtiyaçlarınızı belirlemelisiniz.
  • Eğer tek bir istemciye sahipseniz muhtemelen böyle bir tasarımı uygulama ihtiyacınız hiç bir zaman olmayabilir. İhtiyaçlarınızı doğru analiz etmelisiniz.

Son Söz;

Bu yazıda anlatılanlar belli domainler için uygun olabilirken diğer domainler için uygun olmayabilir. Mimari tasarımınızı kendi iş modelinize göre kurgulamalı ve ihtiyaçlarınıza uygun çözümleri üretmelisiniz.

Günümüzde bağımsız ön yüz uygulamaları, micro frontends gibi farklı yaklaşımlar ve platformların farklılaşan kullanıcı deneyimleri ön yüze uygun API tasarımının gerekliliğini göstermektedir.

Yazıya katkılarından dolayı Fehmi Can Sağlam, Özer Yılmaztekin ve Göksel Ünal’a teşekkürler.

--

--

Oğuz Kılıç

developer @trendyol, ex @eBay, sci-fi addict. JavaScript, Frontend, Software Architecture