--
Bu yazı Lin Clark’ın oluşturduğu
Code Cartoons
adlı yayımınA 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 yazısında burada anlatılanlardan faydalanacağız.
Düzenleme: Yayımladığım Redux yazımın linkini ekledim.
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.
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.
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 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
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.
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.
store
lardispatcher
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.
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