MVI Mimarisi ile Jetpack Compose

Sinan Yılmaz
Huawei Developers - Türkiye
5 min readDec 4, 2023
Photo Anders Jildén by on Unsplash

Giriş

Yazılıma yeni başladığımız zamanlarda genelde hangi dili öğrenmeliyim, hangi dil daha çok kullanılıyor gibi sorularla boğuşuyoruz. Bu soru daha sonraları ise hangi mimariyi öğrenmeliyim olarak devam ediyor.

Aslında bu soruların tam olarak doğru bir cevabı yok. Önemli olan ihtiyaç demek yanlış olmaz. Programlama dilinin araç olması gibi mimarilerde de benzer durum geçerli diye düşünüyorum. Her mimarinin odaklandığı problem çözümü, parlayan bir yönü var. Bu doğrultuda “böyle de bir seçeneğimiz var, işe yarayabilir” mottosuyla bu blogda sizlere MVI’dan bahsetmek istiyorum.

MVI (Model-View-Intent)

MVI, 1990'lı yıllarda ortaya çıkan, reaktif bir yaklaşımla uygulama geliştirmeyi amaçlayan bir yazılım mimarisi. Her mimaride olduğu gibi MVI da ortaya çıkarken bazı fikirlere odaklanıyor. Dilerseniz ilk bunlara bir bakalım.

MVI’ın yapısı bazı noktalarda MVC’ye benzer. Bunun nedenlerinden birisi elbette MVC’den esinleniyor olması.

from cycle.js.org

Yukarıdaki şemada kullanıcı da dahil olmak üzere MVC katmanlarının neler yaptığı bize gösterilmiş. Burada Model ve View’ın birer görevi varken Controller’ın 2 farklı görevi var; Modeli ve View’ı manipüle etmek. Tam da bu konu MVI’ın odaklandığı noktalardan birisi. MVI’ın amacı her katman veri göndermek/almak dışında bir görev üstlenmesin, birbirlerinden o kadar bağımsız olsun.

from cycle.js.org

MVC’nin çıktığı yılları ve ihtiyaçları düşününce kendi yapısı yeterli denebilir. Ancak günümüzdeki ekosistemde bu pek mümkün ve mantıklı değil. MVI’da bu çözüme ulaşmak için ihtiyaç basitçe üstteki şemada gösterilmiş. Bir input işlemi ve ardından output işlemi. Bu pencereden bakınca aslında ihtiyacımız olan; fonksiyonellik.

Şimdi dilerseniz MVI’ın katmanlarını tek tek inceleyelim.

Model

MVI’da modeller diğer mimarilerden daha farklı konumlanıyor. Örneğin MVVM’de model katmanını verileri tutmak için kullanıyoruz. MVI’da ise modeller uygulamanın statelerini de temsil etmekte. Yani hem verileri tutar, hem de app state’i temsil eder. Bu nedenle aralarında ve diğer katmanlar arasında veri akışını sağlamak için değiştirilemez olmalı.

Modellerin sadece veri tuttuğu senaryolarda birtakım problemler olabiliyor. Özellikle büyük uygulamalarda birden çok input/output durumunda sorunlarla karşılaşılabiliyor.

Bu, MVI’ın çözmek için odaklandığı bir diğer konu. Modelleri sadece veri tutan değil aynı zamanda state’i de modellere dahil ederek bu gibi durumların önüne geçmeyi hedefliyor.

HomeState.kt sealed class

Yukarıdaki örnekte de gördüğümüz gibi, Loading, Success ve Error durumlarımızı bir sealed class içerisinde oluşturabiliriz. İşte bu bizim state’imiz.

View

Her mimaride olduğu gibi MVI’da da kullanıcı arayüzünün önemli bir parçaşı View katmanı. Model ve Intent’in birbirinden ayırır ve seperation of concerns’de önemli bir rol oynar.

  • Modelden aldığı bilgileri kullanıcı arayüzünde kullanıcı ile buluşturur.
  • Kullanıcının etkileşimlerini yakalar ve intent katmanına iletir.

Intent

İlk olarak intentler Android’in android.content.Intent sınıfı değildir :)

MVI’daki intentler, uygulamanın stateini değiştiren gelecekteki bir eylemi temsil eder. View’dan gelen kullanıcı etkileşimlerini alıp, modelin anlayabileceği formata dönüştürür ve model katmanına iletir. Yani kullanıcının etkileşimini kapsülleyen bir yapı demek yanlış olmaz.

Şimdi bu bilgiler ışığında yukarıda gösterdiğimiz şemayı nasıl güncelleyebileceğimize bir bakalım.

Kullanıcı bir etkileşimde bulunur. Bu etkileşim intentlere ulaşır.Intentler bu değişiklikleri state/model’e iletir. Bu da daha sonra güncellemeleri view aracılığıyla kullanıcıya sunar.

sealed class NoteIntent {
data class AddNote(val note: String) : NoteIntent()
data class DeleteNote(val note: String) : NoteIntent()
}

MVI’ın Temel Prensipleri

MVI’ın temelini 3 prensiple tanımlayabiliriz. Aslında bunları yukarıdaki satır aralarından da görebilirsiniz.

  • Fonksiyonel
  • Değiştirilemez
  • Reaktif

Bu 3 özellik MVI’ın oluşumu adına önemli noktalar. Kullanıcı etkileşimine bağlı olarak direkt bir çıktı sunma durumu için fonksiyonelliğe ihtiyacımız olduğunu yukarıda görmüştük. Fonksiyonel programlama bize direkt bir değer döner ve bu, hızlı bir süreç için bize yardımcı olur.

Değiştirilemez yani immutable nesneler ise thread yönetimini kolaylaştırır. Reaktif programlama da çalışmayı güvenli bir şekilde paralelleştirir. Her yere güvenli bir şekilde ulaşabiliriz. Reaktif programa ürünü olan RXJava’yı kullanmış olanlar bu duruma zaten aşinadır.

MVI’ın Avantajları

  • Tüm katmanların tek bir yönde ayrı bir görevi olması nedeniyle katmanlar arası ayrım daha iyi olur. Bu sayede seperation of concerns güzel uygulanır.
  • Yukarıdaki ayrımın da etkisi ile test edilebilirlik kolaylaşır.
  • Tek yönlü veri akışı sayesinde tahmin edilebilirlik artar, bir sonraki adımda ne ile karşılaşacığınızı öngörebilirsiniz.
  • Tek yönlü veri akışı sayesinde tutarlı bir kullanıcı arayüzüne olanak sağlar.
  • Tüm katmanların ayrılmasının ardından daha modüler bir yapıda proje kontrolü kolaylaşır ve ölçeklenebilir bir yapıya bürünür.

MVI’ın Dezavantajları

  • Yapısının farklılığından dolayı ilk başlangıçta öğrenme eğrisi diğer mimarilere göre zor olabilir.
  • Ayrı ayrı oluşturulan intent ve stateler belirli bir noktada boilerplate kodu arttırabilir.
  • Boilerplate kodlardan kaynaklı özellikle uygulama büyüdükçe complexity artabilir.

Bir Declarative UI: Jetpack Compose

Declatarative UI’ların gelişimi son dönemde sadece Android değil, iOS’da da oldukça arttı. Jetpack Compose da bunlardan birisi. Compose’da state kavramının önemi oldukça fazla. Özellikle composableların recompose işlemi sırasında kullanmak kaçınılmaz.

Felsefeleri de bu doğrultuda MVI ile örtüşmekte ve MVVM de olduğu gibi MVI’la da güçlü yapılar kurulabilir.

MVI ile Jetpack Compose

  • Bildiğimiz gibi Compose bir Declarative UI ürünü. MVI, Declarative yapılarla uyumlu çalışır.
  • Yukarıda tahmin edilebilir yapıdan bahsetmiştik. MVI ve Compose ile birlikte tahmin edilebilir bir state yönetimine sahip oluruz.
  • MVI’ın da state yapısının da etkisi ile state yönetiminde çok fazla zorlanmayız.

MVI vs MVVM

Her ikisi de ayrı süpergüçlere sahip🦸🏻‍♀️🦸🏻

MVVM -> data binding ve view state yönetimi

MVI -> tek yönlü veri akışı ve güçlü state yönetimi

Ancak tabii ki farklı yönleri bulunuyor. Örneğin kullanıcı eylemleri MVI’da intentler ile yönetilirken, MVVM’de viewmodel üzerinden işlemler yapılır.

State yönetimine bakacak olursak MVI’da tek ve merkezi bir state yönetim nesnesi vardır. MVVM’de de merkezi bir state nesnesi vardır ancak state değişiklikleri doğrudan viewmodeldan yapılır.

Sonuç

MVI mimarisinin yaklaşımı gördüğümüz üzere diğerlerine göre biraz farklı. Bu farklılık özellikle Compose kullanırken birtakım avantajlar sağlayabilir. Önemli olan sizin, projenizin neye ihtiyacı olduğu.

Buraya kadar okuduğunuz için teşekkür ederim, beğendiyseniz clap atmayı unutmayın🙌

--

--