Other languages: 日本語, 한국어Русский.
Bu yazı Lin Clark’ın oluşturduğu Code Cartoons adlı yayımın A cartoon guide to Flux adlı yazısının Türkçe’ye çevirilmiş halidir. Orijinal yazı 29 eylül 2015 tarihinde yayımlanmış, o yüzden yazıyı bu tarihi baz alarak okumanız, daha doğru olacaktır. Keyifli okumalar dilerim, baştan söyleyeyim benim yazılarım gibi eğlenceli değil 😜

Karikatürler üzerinden Flux

Arayüz geliştirme topluluklarının hayatına Redux adında bir `olgu` dahil oldu, bu olgu daha meyve bile değilken; Flux vardı. Flux, bir zamanlar arayüz geliştirme için hem çok popüler hem de az anlaşılan konulardan biriydi.

Bu rehberin amacı; bu az anlaşılan Flux’ı herkesin anlayabilmesini sağlamak.

Bu yazının ardından yayımlayacağım diğer çeviri olan Karikatürler üzerinden Redux rehberinde burada anlatılanlardan faydalanacağız.

Problem

İlk olarak; Flux temel olarak neyi çözmeye çalışıyor? buna cevap vermeye çalışacağım. Bu cevabı vermeden önce ufak birkaç detay vermek yerinde olur.

Flux uygulamamızın içinde kullandığımız verileri işlemek için bir model yani pattern. Facebook bu pattern’i, popüler React projesi ile birlikte geliştirmeye başladı, ikisinin gelişimi senkronize bir şekilde devam etti.

Hatta bu beraber gelişimin etkisiyle React ile proje geliştiren çoğu geliştirici Flux’ı projesine dahil etti ancak bunlar bağımsız olarakta kullanılabiliyordu, sonuçta ikisi ayrı projeler, benzedikleri tek nokta; Facebook’un yaşadığı problemlere çözüm üretmek amacıyla geliştirilmiş olmaları.

Peki Flux ile neye çözüm üretiyordu? Bildirim Hatasına.

Eskiden Facebook’a giriş yaptığınızda, mesaj simgesinin üzerinde bildirim olduğunu görüyordunuz. Mesaj simgesine tıkladığınızdaysa; yeni bir mesaj olmadığını farkediyordunuz. Bildirim bir anda yok oluyordu. Daha sonrasında arayüz ile birkaç etkileşime girdiğinizde örneğin kendi profilinizi görüntülediniz vs. bildirim nedensiz bir şekilde geri geliyordu. Ve siz tekrar mesaj simgesine tıklıyordunuz… yine mi hüsran? harbiden yine hüsran… tekrar yeni bir mesaj olmadığını görüyordunuz. Bu döngü böyle sürüp gidiyordu.

Bu döngü sadece Facebook arayüzünde gerçekleşmiyordu! Facebook yazılım ekibi de aynı döngünün içinde sıkışıp kalmıştı.

Her gün yeni bir çözüm buluyorlar, bildirim hatasını düzeltiyorlardı… daha üstünden çok geçmeden, ÇAT! o da ne! hata geri dönmüş…

Bu bildirim hatası uzunca bir süre “çözüldü” — “çözül(e)medi” şeklinde dolanıp durunca yazılım ekibi bu döngüden çıkmak için kesin bir çözüm aramaya başladı. Bu arayışın sonunda büyük bir değişiklik olmazsa; günü kurtarmalık çözümlerden ötemeye gidemeyeceklerini anladılar.

Büyük bir değişiklik yapmanın karşılığı; sistemsel seviyede bir çözüm üretmek oldu. Peki, nasıl bir sistem olmalıydı? daha önce yaptıkları çözümlerde arayüzdeki aksiyonların tahmin edilemez veya takibi sağlanamaz olması nedeniyle; yapacakları yeni sistemin tahmin edilebilir yani predictable olmasını istediler. Predictable bir sistemle bildirim hatasının karşılarına tekrar tekrar çözül(e)memiş olarak çıkmasını engelleyeceğini düşündüler.

Temel problem

Ekibin tanımladığı temel problem, arayüz verilerinin uygulamanın üzerinden kontrolsüz bir şekilde akıyor olmasıydı.

Not: Bunu Facebook ekibinin Flux’ı tanıtırken paylaştıkları basitleştirilmiş sürümlerden derledim. Eminim ki gerçek mimari farklı görünüyordur.

modeller veriyi view katmanına aktarır

Facebook daha önce kurduğu sistemde veri yani data görüntülemek için; veriyi saklayan modeller yazmıştı ve bu modellerde saklanan veriyi view katmanına aktarıyordu. Kullanıcının, uygulama ile etkileşimi view katmanı üzerinden sağlanıyordu.

Bazen view katmanının kullanıcıdan gelen input yani kullanıcının etkileşimlerine göre veriyi tutan modelleri güncellenmesi gerekiyordu.

Ve bazen de modellerin, başka modelleri güncellemesi gerekiyordu.

Bunun üzerine bazen kullanıcı etkileşimleri başka değişiklikleri tetikliyordu.

Tüm bu süreci, akışı düşününce aklıma koltukta otururken masa tenisi oynuyormuşum gibi geliyor. Bir düşünsenize! masa tenisi oynarken topun nereye ineceğini kestiremiyorsunuz. Görüş açınızın kısıtlı olmasından dolayı masanın kenarından düştüğünü görmeniz bile zorlaşıyor.

View katmanları modelleri günceller, modeller diğer modelleri günceller. Bu koltuktan ayrılmadan masa tenisi oynamaya çok benzer.

Haydi bir de bu bahsettiğimiz view-model değişikliklerinin asenkron olarak gerçekleşebileceğini düşünelim. Bir değişiklik, diğer birçok değişikliği tetikleyebilir.

Biraz önce verdiğim hayali ortama dönelim.

Masa tenisi oynamaya devam ederken masaya bir çanta dolusu ping-pong topu fırlatılıyor! Uçuşan, rastgele yönlere giderek yere düşen toplar ve onların birbirine çarparak oyunu tamamen berbat etme ihtimali.

Bu fırlatılan topları asenkron olarak çalıştırdığımız değişiklikler olduğunu düşünürseniz, böyle bir uygulama içerisindeki veri akışını debug etmek yani hatalarını ayıklamak çok zor.

Çözüm: tek yönlü veri akışı

Dolayısıyla Facebook, bu probleme çözüm olarak farklı bir mimari yapı kurmaya karar verdi, bu yeni mimariye göre uygulama içerisindeki tüm veri akışı tek bir yönde hareket edecekti. Ne zaman uygulamanıza yeni bir veri eklemek isterseniz, verinizin akışı baştan-sona doğru hareket etmek zorunda olacaktı. Böyle bir mimarı tasarladılar ve ona Flux ismini verdiler.

Bu diyagramı Facebook’un Flux dökümanlarında bulabilirsiniz, kendisi gözüktüğünden daha güzel.

Bu diyagram harika… ama Flux’a aşina değilseniz; muhtemelen şu an yukarıdaki diyagram için güzel şeyler söylemeyeceksiniz.

İlk bakışta anlamak için bu diyagram, dolayısıyla Flux’ın dökümantasyonu pek yardımcı olmuyor. Bana göre diyagramlar bir sistemin nasıl çalıştığını gösteren büyük bir resim çizdiği gibi kendini de anlatmalı, kolay anlaşılmalı.

Benim Flux’ı anlamama sağlayan şey, maalesef diyagram veya dökümantasyonu değildi. Anlamak için belirli bir hedefe ulaşmaya çalışan bir takım hayal ettim, sizlere Flux’ı anlatırken bu takımı anlatacağım. Bu takım bir sistem içerisinde aynı hedef doğrultusunda birlikte çalışıyor olacaklar. Şimdi size bu hayal ettiğim sistemi ve çalışanların karakterlerini anlatacağım.

Karakterlerle tanışma keyfi

Karakterlerin birbirleri arasında nasıl bir etkileşim olduğunu açıklamadan önce onları tanıtan hızlı bir giriş yapayım.

The action creator

İlk karakterimiz action creator yani eylem başlatıcı. İlk olması ve isminde başlatıcı geçmesinin sebebi sistemin akışın onunla başladığına dair fikir veriyordur umarım. Bu karakterin görevi, uygulamamızın arayüzündeki tüm değişiklik ve etkileşimlerin yakalanıp, Flux sistemine aktarılması için bir eylem başlatmak.

Uygulamanız içerisinde bulunan herhangi bir veriyi veya görünümü değiştirmek isterseniz bir action create etmeniz gerekiyor.

Action Creator bir telgraf operatörüne benziyor. Mesajınızı sizin için biçimlendiriyor.

action creator karakterini bir telgraf operatörü olarak hayal ediyorum. Göndermek istediğiniz mesajı bilerek telgraf operatörüne gidersiniz ve o mesajınızı telgraf sistemine göre biçimlendirir. action creator Flux mimarisinde aynı görevi gerçekleştiriyor. Siz elinizde mesajınızla gidiyorsunuz, o mimarinin anlayacağı şekilde biçimlendiriyor.

action creator yanına gittiğinizde sizden iki farklı bilgi talep ediyor. ilk olarak type yani başlatmak istediğiniz eylemin türünü belirtiyorsunuz. Bu eylem türleri mimarinizin içerisinde daha önce tanımlanmış olmalı. Geliştiriciler genel olarak sistem içerisinde constants.js diye bir dosya içerisinde constant yani sabit değerler tanımlıyorlar. Örnek olarak MESSAGE_CREATE ve MESSAGE_READ şeklinde iki farklı eylem türü tanımlayabiliriz. Uygulamamız içerisinde yeni bir mesaj ekleme işlemi olduğunda ilk eylem türünü, mesaj karşı taraf okunduğunda ikinci eylem türünü action creator'a vermemiz gerekiyor.

Görüldüğü üzere mimari içerisinde herhangi bir eylem başlattığınızda o bir türe ihtiyaç duyuyor. Bu türler değişemez birer sabit olarak tanımlanıyorlar ve biz kullanıcının arayüzümüzdeki tüm etkileşimlerini önceden bilebiliyor, tahmin edebiliyor oluyoruz. Bu harika bir olay!

Düşünsenize, büyük bir projeye dahil oldunuz, projede kullanıcının girebileceği tüm etkileşimleri okuyabileceğiniz bir dosya var. Eylem türlerinin saklandığı dosyayı okuyarak, sistemin sağladığı olası tüm değişiklikler kolayca anlaşılabilir.

İki farklı bilgi talep ediyor demiş idim. İlki eylemin türü idi, ikincisi ise eylemin payload’ı yani taşıdığı yükü. payload genelde bir obje olarak tutuluyor. Örnekler üzerinden devam edersek, MESSAGE_CREATE action türünde payload objesinin içerisinde yaratılacak mesajın içeriği olabilir.MESSAGE_READ action türünde payload objesinin içerisinde mesajın okunup/okunmadığını bildiren boolean true/false bir değer olabilir. {type:MESSAGE_READ, payload:{is_read:TRUE}}

action creator sistem üzerinde bir eylem başlattığında, bu eylem sistemin bir diğer karakteri dispatcher yani dağıtıcıya aktarılıyor.

The dispatcher

Dispatcher yani dağıtıcının görevi gelen istekleri büyük bir kayıt defteri içerisinde tutarak onları gerekli merciye yönlendirmek.

Bu karakteri telefon santralinde çalışan bir telefon operatörüne benzetiyorum. Telefon santralleri, tüm bağlanabilecek dahililerin listesini tutar ve bir istek geldiğinde bu dahili bağlantılara yönlendirme işlemini gerçekleştirir. Bizim sistemizin içerisinde dahililerin listesi yerine verileri sakladığımız store ların bir listesi tutuluyor. Ne zaman dispatcher a action creator dan bir eylem gelirse, o eylemi gerekli store a yönlendirir.

Eylemlerin ihtiyaç duyduğu yönlendirmeler için tüm sistemin kaydını elinde tutuyor. Ne zaman dağıtıcıya action creator yani eylem başlatıcıdan bir eylem gelse, gelen eylemi farklı bir hafızaya yönlendirir.

Bu yönlendirmeyi senkronize bir şekilde yapar, birden çok ping-pong topunu masaya fırlatma örneğindeki yaşananları düşünürseniz, topların birbirleriyle çarpışma ihtimalini bu şekilde ortadan kaldırmış oluyoruz.

Ve eğer store lar arasındaki bağımlılıkları ayarlamanız gerekiyorsa biri diğerinden önce güncellenebilir gibi durumları dispatcher waitFor() özelliğiyle sizin için halledebiliyor.

Flux’ın dispatcher ı diğer birçok mimarinin dispatcher ından farklı. Eylem, eylem türü ne olursa olsun tüm kayıtlı store lara gönderilir. Bu, store un sadece kendisine tanımlı eylemlere abone olmaması anlamına geliyor. Her store tüm eylemleri dinler ve o eylem ile alakalı bir işlem yapması gerekiyorsa; yapar.

The store

Sıradaki store yani depo. store uygulamamızın tüm state lerini saklar ve state değiştirme mantıkları(logic) store içinde tutulur.

State nedir? diye bilmeyenler için ufak bir paragraf açayım. React içerisinde her component birer Class ve bu Class’lar içerisinde state yani bir obje tutulabiliyor. Bu state objesi private ve tamamen component tarafından yönetilen bir obje.
The store aşırı kontrolcü bir bürokrattır. Tüm değişiklikler onun üzerinden geçmelidir.

Ben store u aşırı kontrolcü bir bürokrat olarak düşünüyorum. Tüm state değişiklikleri mutlaka bizzat kendisi tarafından yapılmalıdır. Ve siz doğrudan state i değiştirmesini talep edemezsiniz. store içinde setters yani ayarlayıcı bulunmuyor. Bir state değişikliği talebinde bulunmak için, uygun prosedürü izlemek zorundasın… action creator / dispatcher yolunu takip ederek bir eylem göndermelisin.

Yukarıda bahsettiğim gibi, eğer bir store bir dispatcher ile kayıtlıysa, tüm eylemler ona gönderilecektir. Genel olarak her store kendi içerisinde bir switch ifadesine(switch ifadesini bilmeyenler için) sahiptir, bu ifadenin amacı eylem store ‘a ulaştığında action creator üzerinde tanımlanan eylem türüne bakıp store içinde birşey yapıp/yapmayacağına karar vermektir. Eğer store ona ulaşan eylemi bu switch ifadesinde yakalıyorsa, bu eylemi temel alarak hangi değişikliğin yapılmasını gerektiğini belirleyecek ve state ini güncelleyecektir.

store ne zaman state üzerinden bir değişiklik yaparsa, bir değişim event i yaratır. Bu event in amacı the controller view karakterine state in değiştiğini bildirmektir.

The controller view and the view

the view lar uygulamanın state ini almak ve onu kullanıcıya sunmaktan sorumludur.

the controller view, store ve view arasındaki bir orta bulucu gibidir. store'dan bildirimler alır ve aldığı veriyi altındaki view'lara dağıtır. view aldığı datayı kullanıcıya sunar.

the view ı sadece sunum yapmakla görevli birine benzetebiliriz. Uygulamadaki herhangi bir şeyin farkına varamaz, sadece kendisine teslim edilen verileri ve o verileri insanların anlayabileceği çıktılara nasıl dönüştürebileceğini(HTML ile) bilir.

the controller view store ve view arasındaki bir orta bulucu gibidir. store ne zaman uygulamanın state inin değiştiğini söyler. Yeni state i alır ve elindeki güncellenmiş state i altındaki tüm view lara dağıtır.

Birlikte nasıl çalışıyorlar

Haydi artık tüm bu tanıdığım karakterlerin birlikte nasıl çalıştıklarına bir göz atalım.

Kurulum

İlk olarak kurulumla başlayalım: uygulama mimarimizi başlatmamız sadece bir kez gerçekleşir.

  1. store lar dispatcher a bir eylem geldiğinde mutlaka haber edilmek istediklerini bildirirler.

2. Ardından controller view lar store lardan en son state leri isterler.

3. store lar controller view lara state leri teslim ettiğinde, controller view lar state i kullanıcıya sunmaları için altındaki view lara aktarırlar.

4. Ayrıcacontroller view lar store lara state değiştiğinde mutlaka haber edilmek istediklerini bildirirler.

Veri akışı

Kurulum aşaması tamamlandıktan sonra artık uygulamamız kullanıcının etkileşime girmesine hazırdır. Bu nedenle haydi kullanıcıya bir değişiklik yaptırarak bir eylem tetikleyelim.

Bir kullanıcı etkileşimi ile veri akışını başlatacağız.

  1. the view action creator a bir aksiyon hazırlamasını söyler.

2. the action creator eylemi biçimlendirir ve dispatcher a gönderir.

3. the dispatcher eylemi sırayla store lara gönderir. Her store tüm eylemleri dinler. Sonra the store eyleme kendi içerisindeki switch ifadesine göre kendi içindeki state i değiştirmek veya değiştirmemeye karar verir.

4. state değiştikten sonra store ona bağlı olan controller view lara haber verir.

5. Ardından bu controller view lar store dan güncellenmiş olan state i kendilerine vermelerini isteyeceklerdir.

6. store güncellemiş olduğu state i verdikten sonra, controller view altındaki view lara kendilerini güncellenmiş state i baz alarak yenilemelerini söyler.

İşte Flux’ı nasıl düşündüğüm ve anladığım. Umarım bu size yardım eder!


Sırada Redux yazısı…

Haftaya Redux yazısını yayımlamayı planlıyorum. Takipte kalmak için beni twitter üzerinden takip edebilirsiniz.

İlk defa detaylı bir çeviri yapmış oldum, umarım güzel bir çeviri olmuştur. Geri dönüşleriniz varsa memnuniyetle dinlerim, Redux, React veya yardımcı olabileceğimi düşündüğünüz bir konu veya bir sorunuz var ise iletişime geçmekten çekinmeyin lütfen :) Sevgiler