Vuex logosu - Vue.js uygulamaları için state management kütüphanesi

Canva’da logolar kullanılarak hazırlanan görsel.

Bir çılgınlık yapıp bambaşka bir sektörde faaliyet gösteren, bambaşka bir teknoloji yığınına sahip bir şirkete, bambaşka bir unvanla geçtiğimi şu yazıda uzun uzun anlatmıştım. Native Android Developer iken, Vue.js ve Quasar ile geliştirme yapan bir ekibe takım lideri oldum. Sprint planlama, çevik (agile) metodolojileri uygulama ve yöneticilik konularında kendimi geliştirip sıfırdan Vue.js öğrendiğim yetmedi, veri tabanı fonksiyonları bile yazdım (yazıyorum). An itibariyle burada (Eylül 2024) 8. ayımı doldurdum.

Tam Olarak Ne Yapıyoruz? Mevbank Neo Nedir?

Bana “Hukuk şirketinde ne yapıyorsun?” diye soranlara toplu cevap vermek için bu paragrafı yazıyorum. Kanun, KHK, yönetmelik, tebliğ ve benzeri yasal düzenlemelerden oluşan mevzuatın takibi, ülkemizde mevzuat değişikliklerinin çok sık yapılması ve hukuk sisteminin karışık olması gibi nedenlerle hem zordur hem de şirketlerin iş akışlarının hukuksal sebeplerden ötürü aksamaması için gereklidir.

Mevbank Neo, 10 milyon içtihatı, uzman makalelerini, Resmî Gazete’de yayımlanan değişiklik ve ilaveleri abonelerinin kullanımına sunan bir web sitesidir. Bir başka ifadeyle, büyük veri (seversiniz “big data” demeyi :)) içerisinde arama yapabildiğiniz, belgelerin içeriğini ve ne zaman değiştiğini görebildiğiniz tarayıcı tabanlı bir uygulamadır. Böylece aboneler, işlerini yürürlükteki yasal düzenlemelere göre düzenleyerek iş kalitelerini, güvenilirliklerini ve verimliliklerini arttırırlar.

Peki Mevzuat Değişiklikleri Mevbank Neo’ya Nasıl İşleniyor?

Mevzuat değişikliklerini Resmî Gazete ve benzeri devletimize ait birkaç resmî web sitesi üzerinden takip etmekten ve bunları sisteme eklemekten sorumlu ekibimiz, zamanında yazılmış bir masaüstü uygulaması aracılığıyla bu değişikliklerin çoğunu elle giriyordu. Bu veri giriş uygulamasının biraz eski kalmasından mütevellit, artık ihtiyaçları karşılamadığı ve yenilenmesi gerektiğine karar verilmişti.

Yeni veri giriş projesini de Vue.js ile yazacaktık. Vue.js’in en güçlü yanı bileşen tabanlı olmasıydı. Uzun kod bloklarını engellemenin en güzel yolu ise SOLID prensiplerine uyumlu olarak kod bloklarını işlevlerine göre küçük ve bağımsız parçalara ayırmaktı. Bu bileşenler arasındaki bağlantıyı tabii ki bir durum yönetimi (state management) kütüphanesi sağlayacaktı. İşte böyle başlıyor Vue.js’te kullanılan durum yönetimi kütüphanesi Vuex ile hikâyemiz.



Yazılımda Neden Durum Yönetimi Araçlarına İhtiyaç Duyarız?

Satranç tahtasında konumlanmış siyah at ve diğer taşlar, stratejik planlama ve tasarım desenlerini temsil ediyor.

Fotoğraf: Piotr Makowski / Unsplash

  • Veri Tutarlılığı: Bir uygulama içinde birden fazla bileşen aynı veriyle ilgileniyorsa bu verilerin tutarlı bir şekilde yönetilmesi gerekir. Durum yönetimi araçları, bu verilerin merkezî bir yerden yönetilmesini sağlar ve tüm bileşenlerin aynı kaynaktan güncellenmesini garanti eder.

  • Durumun İzlenebilirliği: Durum yönetimi araçları, uygulamanın durumunu ve durum değişikliklerini izlemeyi ve kaydetmeyi, dolayısıyla hata ayıklamayı ve uygulamanın nasıl çalıştığını anlamayı kolaylaştırır.

  • Tek Yönlü Veri Akışı: Durum yönetimi araçları genellikle tek yönlü veri akışı prensibini destekler. Bu, verilerin sadece bir yönde hareket ettiği anlamına gelirken uygulamanın durumunu takip etmeyi ve yönetmeyi daha öngörülebilir hâle getirir.

  • Kodun Temizliği ve Bakımı: Merkezî bir durum yönetimi, kodun daha modüler ve yönetilebilir olmasını sağlar. Durum değişiklikleri merkezî bir yerden yapıldığı için, kodun farklı bölümleri arasındaki bağımlılıklar azaltılır (low coupling) ve bu da bakım ile yeni geliştirmeler eklemeyi kolaylaştırır.

  • Performans: Durum yönetimi araçları, performansı optimize etmeye yardımcı olabilir. Örneğin, sadece ilgili bileşenler durum değişikliklerinden haberdar edilir ve tüm sayfanın gereksiz yere yeniden render edilmesi önlenir.

  • Büyük ve Karmaşık Uygulamalar: Uygulama büyüdükçe ve karmaşıklaştıkça durum yönetimi daha zor hâle gelir. Durum yönetimi araçları, bu zorluğun üstesinden gelmek için ölçeklenebilir çözümler sunar.

  • UI ve İş Mantığının Ayrılması: Durum yönetimi araçları, kullanıcı arayüzü (UI) ve iş mantığını birbirinden ayırmayı kolaylaştırır. Bu, UI bileşenlerinin sadece görsel temsil ile ilgilenmesini ve iş mantığının (lojik) merkezî bir yerde yönetilmesini sağlar.

Hiçbir durum yönetimi aracı kullanmayan projelerde çeşitli zorluklar yaşadıktan sonra, yeni projede bir durum yönetimi aracı kullanmamız gerektiğini düşünüyordum. Gözlerim, takım lideri olduğum ekipte ve sıfırdan başlanacak bir projede, yeni ve karmaşık, binlerce satırdan oluşan bileşenleri kaldıramayacaktı. Kodu daha temiz ve düzenli tutmazsak ilk yöneticilik tecrübemde ekibe yeni bir bakış açısı ve anlayış katamayacağım için kendimi başarılı saymayacaktım.

Bir gün, yöneticisi olduğum çalışma arkadaşlarıma Vuex’i anlatmaya karar verdim ve nereden başlayacağımı düşündüm. Tasarım kalıpları hakkında üniversitede ders alıp almadıklarını sordum. Alamadıklarını öğrendiğimde, kendilerini geliştirmeyi ve yeni şeyler öğrenmeyi seven insanlar oldukları için anlatmaya buradan başlamaya karar verdim. Önce tasarım kalıplarını, sonra Vuex’i, ardından da Vuex’te yer alan tasarım kalıplarını konuştuk. Eğitim çok beğenilince okuduğunuz bu yazıyı kaleme aldım.

Yılların Android geliştiricisi olarak Android’de durum yönetimi için kullanılan pek çok araç ve kütüphane olduğunu tabii ki biliyorum. ViewModel, SharedViewModel, LiveData, StateFlow, MutableStateFlow, UnidirectionalDataFlow (Compose)… Vue.js’te de birkaç tanesinden bahsediliyordu. Vuex ise favori olandı.

  • Veri giriş projesinde zengin metin editörünün bulunduğu bileşen,

  • Zengin metin editörünün sağ tarafında, Mevbank Neo’daki arama sayfasında kullanılacak filtre kriterlerinin girileceği bir belge özellikleri bileşeni,

  • Önceki maddelerde bahsettiğim bileşenlerle birlikte, ”kaydet” butonu, başlık girdisi (input) gibi daha genel ayarları içeren bir yeni belge (klasik CRUD create) sayfası,

  • Yeni belge sayfasında kullanılacak zorunlu girdileri, sayfa açılmadan önce kullanıcıdan alacak olan bir popup…

Daha projenin ilk gününde sayabileceğim dört farklı bileşen, durum yönetimi kütüphanesinde tutulacak olan yeni belge değişkenini güncelleyecek veya kullanacaktı. Sonradan bu sayı arttı. Yazının uzun olmaması ve gizli bilgi vermek istemediğim için bu kadar yeterli. Kullandığımız zengin metin editörü hakkında da birkaç yazı yazmayı düşünüyorum.

Durum yönetimi kütüphanesi kullanmadan bu işi yapalım desek “prop”lar ve “watch”lar ile sürekli veri aktaracağız ve en az dört bileşen arasında yapacağız. Birindeki değişikliğin anında hepsine iletilmesi ve sayfanın eş zamanlı bir şekilde güncellenmesi… Bu, çok fazla boilerplate kod demek.



Vuex Nedir

Grafik yazar tarafından hazırlanmıştır.

Grafik yazar tarafından hazırlanmıştır.

Vuex, Vue.js uygulamaları için tasarlanmış bir durum yönetim kütüphanesidir. Vuex, uygulamanın tüm bileşenlerinde paylaşılan merkezi bir durum (state) deposu sunar ve bu durumu yönetmek için belirli kurallar getirir. Bu merkezi yapı, özellikle büyük ve karmaşık Vue.js projelerinde bileşenler arasında veri paylaşımını ve senkronizasyonunu kolaylaştırır.

Vuex’in Temel Kavramları:

1. State (Durum): Uygulamanın merkezî durumunun saklandığı yerdir. Vuex, tüm bileşenlerin erişebileceği tek bir yerden durumu yönetir.

2. Mutations (Değişiklikler): Durumun değiştirildiği yerdir. Mutations, Vuex’te durumu değiştirebilen tek yer olup senkronize işlemlerle durum değiştirilebilir. Her mutation bir isim alır ve parametre olarak bir “payload” alabilir.

3. Actions (Eylemler): Mutations’ı tetikleyen işlemlerdir. Genellikle asenkron işlemleri (örneğin bir API çağrısı) yürütmek için kullanılır. Actions tamamlandığında, mutations aracılığıyla durumu günceller.

4. Getters: Vuex’teki durumu, bileşenlerde kullanmadan önce işlemden geçirmek veya hesaplanmış değerler elde etmek için kullanılır. Getters, durumun türetilmiş hâlini sağlar.

5. Modules (Modüller): Büyük ölçekli uygulamalarda Vuex store’unu daha yönetilebilir hâle getirmek için, state, mutations, actions ve getters’ı modüllere bölebilirsiniz. Her modül kendi state, mutations, actions ve getters’ını içerebilir ve hatta alt modüllere sahip olabilir.

Vuex basitçe şu şekilde kurulur:

javascript
// src/store/index.js
import { createStore } from "vuex";

const store = createStore({
  state: {
    article: {
      title: null,
      content: null
    }
  },
  mutations: {
    updateArticle(state, newArticle) {
      state.article = { ...state.article, ...newArticle };
    }
  },
  actions: {
    updateArticle({ commit }, newArticle) {
      commit("updateNewArticle", newArticle);
    },
  },
  getters: {
    newArticle(state) {
      return state.newArticle;
    }
  }
});

export default store;
javascript
// src/boot/store.js
import { store } from "quasar/wrappers";
import storeConfig from "src/store";

export default store((/* { ssrContext } */) => {
  return storeConfig;
});
javascript
// herhangi bir vue dosyası
<template>
  <div v-model="newArticle.title"/>
</template>

import { mapState } from "vuex";
.
.
.
computed: {
  ...mapState(["newArticle"]),
},
javascript
// quasar.config.js
boot: ["axios", "store"],


Tasarım Kalıbı Nedir?

Yazının akışı için, ufak da olsa tasarım kalıplarının bir tanımını yapmam gerekiyor ancak bu kavram üzerine çok fazla kaynak, hatta kitaplar bulunduğu için yazının çok uzun olmaması ve konunun dağılmaması adına kısa bir özet geçeceğim.

Design pattern (tasarım kalıbı), yazılım geliştirme süreçlerinde karşılaşılan tekrar eden sorunlara çözüm getiren, iyi tanımlanmış ve kanıtlanmış yöntemlerdir. Yazılımcıların daha önce karşılaşılan sorunları yeniden keşfetmek yerine, bu kalıpları kullanarak daha yapılandırılmış ve sürdürülebilir çözümler üretmelerine olanak tanır. Design pattern’ler yazılım projelerinde kodun okunabilirliğini, yeniden kullanılabilirliğini ve bakımını kolaylaştırmayı amaçlar.



Vuex Hangi Tasarım Kalıplarını Kullanır?

Kırmızı ve koyu mavi tonlarında soyut akışkan tasarım, dinamik ve enerjetik bir görsel atmosfer oluşturuyor.

Fotoğraf: Cassi Josh / Unsplash

Vuex’i keşfettiğimde bir durum yönetimi aracından daha fazlası olduğunu gördüm. Tam olarak ne olduğunu araştırırken içinde kullanılan tasarım kalıplarını merak ettim. Vuex kullanan bir geliştirici, aşağıdaki tasarım kalıplarını hiç farkında olmadan kullanmaktadır:

1. Observer Pattern:

Vuex, tepkisel (reactive) durum yönetimi sağlar. Vue bileşenleri, durumdaki değişiklikleri “gözlemler” ve buna göre kendilerini günceller.

2. Singleton Pattern:

Vuex store, uygulama boyunca tek bir örnek olarak kullanılır ve bu örnek üzerinden durum yönetimi yapılır. Bu sayede, merkezi bir durum yönetimi sağlanır.

3. Module Pattern:

Vuex, büyük uygulamalarda durumu modüller halinde organize etmeye olanak tanır. Her modül kendi state, mutations, actions ve getters’ına sahip olabilir. Bu, kodun daha modüler ve yönetilebilir olmasını sağlar.

4. Façade Pattern:

Vuex, Vue bileşenleri ile durum yönetimi arasındaki karmaşıklığı gizler ve daha basit bir arayüz sunar. Bileşenler, doğrudan durum yönetimi detaylarına girmeden Vuex aracılığıyla durum ile etkileşime geçer.

5. Command Pattern:

Vuex’te actions, mutations aracılığıyla durum üzerinde değişiklikler yapar. Actions, asenkron işlemleri ve iş mantığını içerirken mutations doğrudan durum değişikliklerini gerçekleştirir. Bu yapı, command pattern’e benzer.

Vuex’in kullandığı tasarım kalıpları içinde özellikle Observer, çok sevilen ve iş görüşmelerinde çok sorulan, yazılımcıların mutlakak bilmesi gereken bir kalıptır. Şimdi, Observer tasarım kalıbına bir göz atalım.



Observer Tasarım Kalıbı

Nesneler arasındaki “birine bağlı olarak diğerinin güncellenmesi” ilişkisini kuran bir tasarım kalıbıdır. Basitçe, bir nesne (subject) üzerinde bir değişiklik olduğunda, bu nesneye “abone olmuş” olan diğer nesneler (observers) bu değişiklikten haberdar olur ve kendilerini güncellerler.

Şöyle düşünebilirsin: Bir haber sitesine abone olduğunda, yeni bir haber yayınlandığında sana bir bildirim gelir. Haber sitesi “subject”, sen ise “observer” rolündesindir. Her yeni haberde sana otomatik olarak bir bildirim gelir, yani bir olayı “gözlemlersin”.

Bu kalıbın temel yapısı üç parçadan oluşur:

1. Subject (Konu/İzlenen): Değişikliği takip edilen nesne. Yukarıdaki örnekte newArticle değişkeni izlenen nesne oluyor.

2. Observer (Gözlemci): Subject’te bir değişiklik olduğunda haberdar olan nesneler. Yukarıdaki örnekte newArticle’ı kullanan Vue dosyası veya dosyaları.

3. Notify (Bildirim): Subject, her değişiklikte gözlemcilerine haber gönderir.

Bu yapı, uygulamalarda reaktif sistemler kurmak için kullanılır. Örneğin, newArticle değişkenini kaç yerden düzenlersek düzenleyelim, daima veri ve ekran stabil ve güncel kalacaktır. Observer tasarım kalıbının Vuex ile kullanımına bir örnek verelim:

javascript
// Vuex store'daki state
const store = createStore({
  state: {
    newArticle: {
      title: '',
      content: ''
    }
  },
  mutations: {
    updateArticle(state, article) {
      state.newArticle = article;
    }
  }
});

// Vue dosyasındaki component
export default {
  computed: {
    ...mapState(['newArticle'])
  },
  watch: {
    newArticle(newVal) {
      console.log('Article updated:', newVal);
    }
  }
};

Trafik ışığı örneği, Observer tasarım kalıbını açıklamak için sıkça kullanılan basit ve anlaşılır bir örnektir. Bu örnekte, trafik ışığı nesne rolünde, yoldan geçen arabalar ise izleyici rolündedir.

Trafik ışığı (nesne), belirli zaman aralıklarında renk değiştirir (kırmızı, sarı, yeşil). Renk değiştiğinde yoldan geçen araçlar (izleyiciler) bu değişikliği gözlemler ve buna göre hareket ederler. Örneğin:

  • Kırmızıya döndüğünde araçlar durur.

  • Yeşile döndüğünde araçlar hareket eder.

  • Sarıya döndüğünde ise araçlar hızlarını düşürüp durmaya hazırlanır.

Trafik ışığı değiştiğinde, araçlar ve yayalar anında bu değişiklikten haberdar olur ve buna uygun davranış sergiler.

Bir e-ticaret sitesinde müşteri bir ürünü sepete eklediğinde, sepetin durumu (nesne, subject) değişir. Bu durumda, sepete bağlı farklı bileşenler (observers, izleyiciler), sepetin güncellenmesiyle anında tepki verir. Örneğin, kullanıcı bir ürünü sepete eklediğinde, sepet bileşeni ürünlerin sayısını ve toplam fiyatı günceller; ödeme sayfası ise anında yeni fiyatı gösterir. Ayrıca, stok durumu da kontrol edilir ve ürünün stok adedi düşürülerek stok sayfası buna göre güncellenir. İşte bu yapı, Observer Pattern’in tipik bir kullanımını temsil eder: Sepet değiştiğinde, bu değişiklikten haberdar olan tüm bileşenler tepki verir ve arayüz otomatik olarak güncellenir.



Sonuç:

Vuex ve içinde barındırdığı design pattern’ler, büyük ve karmaşık Vue.js projelerinde verimli ve sürdürülebilir bir yapı sağlar. Özellikle Observer gibi kalıplar, uygulamalarda reaktif ve tutarlı bir veri akışı sağlamada kritik rol oynar. Vuex’in merkezi bir durum yönetimi sunması, veri tutarlılığını garanti altına alırken bileşenler arasındaki veri paylaşımını da kolaylaştırır. Bu yapı, aynı zamanda kodun daha modüler, anlaşılır ve bakımının kolay yapılabilir olmasını sağlar.

Tasarım kalıpları, yazılım geliştirme sürecinde karşılaşılan sorunlara hazır çözümler sunarak yazılımcıların daha önce kanıtlanmış yöntemleri kullanmasına olanak tanır. Özellikle Observer, modern uygulamalarda veri akışını ve bileşenlerin güncellenmesini sadeleştirir. Vuex’in bu kalıpları kullanarak sağladığı faydalar, özellikle büyük ölçekli projelerde ve ekip çalışmalarında daha belirgin hâle gelir. Bileşen tabanlı bir yapı kurarken durum yönetimi ve veri akışını doğru bir şekilde yönetmek, projelerin hem geliştirme sürecini hızlandırır hem de gelecekteki değişikliklere daha kolay uyum sağlamasına yardımcı olur.

Sonuç olarak, Vuex’in içinde yer alan design pattern’ler, hem yazılımcıların hem de ekiplerin işini kolaylaştıran yapı taşlarıdır. Vuex gibi bir durum yönetimi aracı kullanmak, projeyi hem ölçeklenebilir hâle getirir hem de yazılım mimarisini sağlam temeller üzerine inşa eder. Bu nedenle, sadece Vue.js projelerinde değil, genel yazılım geliştirme süreçlerinde de tasarım kalıplarını anlamak ve doğru kullanmak, yazılımcıların daha kaliteli, sürdürülebilir ve performanslı uygulamalar geliştirmesini sağlar.