6- Modularization in QNB Android Mobile📢

Osusamcioglu
IBTech
Published in
6 min readAug 28, 2024

Herkese merhaba, ben Ozan 👋

Sürekli gelişen ve değişen teknoloji trendlerine ayak uydurmak projelerin ömürlerini uzatıyor, bakım maliyetlerini azaltıyor. Ancak bu süreç bazen zorlayıcı olabiliyor. Bu yazıda önerilen modül yapısına sahip olmayan bir bankacılık uygulamasında, Google’ın önerdiği multi-module yapısına geçme kararımızdan, yaşadığımız zorluklardan ve öğrendiklerimizden bahsedeceğim.

Eski Modül Yapımız Nasıldı? ⏪

Bankacılık uygulaması olan projemiz uzun yıllardır yaşayan ve güncellenen bir projedir. Eski modül yapımız;

  • Legacy: Java ağırlıklı kodların olduğu, eski sayfaların yer aldığı ve 3 modülden oluşan bölüm.
  • Features: 16 modülden oluşan bu bölüm, ilgili akışların activity, view model gibi UI katmanlarını içeren kotlin base olarak yeni mimarimizde yazdığımız ekranlardan oluşan modullerdir.
  • UIComponent: Projede oluşturduğumuz componentleri içeren modüldür.
  • Mobile-Core: İçerisinde projede kullanılan tüm Util, Helper, Base, Generic, Navigation, Network gibi core sınıfların yer aldığı modüldür. Bu modülde core sınıflar dışında feature’lara ait Data katmanlarını da içermektedir.

Bu Çalışmaya Neden ve Nasıl Başladık? 🤔

Eski modül yapımızda da görüleceği gibi;

  • Feature-Legacy, MobileCore-Legacy gibi bağımlılıklardan dolayı paralel build almak oldukça zorlaşıyor, build süreleri gün geçtikçe artıyordu
  • Legacy bölümü MobileCore erişemediği için tamamen projeden atıl kalmış, yardımcı sınıflara doğrudan erişemiyordu. Kod çoklama vs gibi çözümlerle ilerlenmek zorunda kalınıyordu.
  • Tüm yardımcı sınıflar tek bir modül içerisinde toplandığı için Mobile-Core oldukça büyüyor; bakım ve yenileme daha da zorlaşıyor, kod geliştirme maliyetleri her geçen gün artıyordu. Bu kadar büyük bir modül için ilgili sorumluluklar atanması ve yeni trendlere uyumlama, modüle hakim olmak zorlaşıyordu.
  • Tüm feature modüllerinin data katmanı Mobile-Core altında yer aldığı için, her modül geliştirme sürecinde burada değişiklik yapıyor ve build süresi uzun olduğu için ciddi zamanlar kaybediliyordu.

Bu nedenlerden dolayı, uygulamamız canlı ortamda olmasına rağmen modül yapısında radikal bir değişikliğe gitmeye karar vermiştik ve takım olarak da bu meydan okumaya hazırdık.

Mimari Seçimi

Çalışmalara başlamadan önce, Google tarafından önerilen çoklu modül mimarilerini inceledik.

  • Data-Domain Modül Yapısı: Bu yapıda, her feature’e ait data ve domain modülleri açılmalıdır. Data modülünde feature’a ait olan data, repo, api gibi sınıfları içerirken, domain modülleri ise UI katmanına data modüllerinden aldığı sınıfları sunmakla görevlidir. Domain katmanında UseCase gibi sınıflar yer alarak network sürecinde mapper, helper vs network sürecinde yapılması gereken işlemlerin yükünü ViewModel üzerinden alır ve karmaşıklığı oldukça azaltır.
  • Data Modül Yapısı: Önerilen bir diğer çözüm olan bu yapıda; Data, Repo, Api, UseCase aynı modül içerisinde yer alır ve UI katmanına veriler bu katman üzerinden sunulur.

Mobil bankacılık uygulamamızda; sunucudan gelen veriler mobil uygulama üzerinde hazır kullanılacak halde olduğu için Domain katmanına bizim projemizde ihtiyaç olmadığına karar verdik. (Domain katmanında yapılacak işlemler backend tarafında yapılmaktadır.)

Bağımlıkların Tespit Edilmesi

  • Feature-Legacy, MobileCore-Legacy modüllerinin bağımlıkları tespit edildi ve bu bağımlılıklar kopartıldı

Modülleri Belirlenmesi ve Oluşturulması

  • Her feature ait data modülleri oluşturuldu ve MobileCore altından data katmanları ilgili modüllere taşındı
  • Core altında yer alacak modüller belirlendi. Navigation, Database, Network, Util gibi başka uygulamalarımızda da alt yapımızı oluşturacak modüller belirlendi ve açıldı. İlgili sınıflar bağımlılıkları kopartıldıktan sonra bu modüllere taşındı
  • Satın alınan veya uygulamaya eklenen SDK’lerin proje içerisinde derli toplu olması ve entegrasyon/kullanım gibi sınıfların tek yerde yönetilmesi için SDK-Impl modülü oluşturuldu
  • Uygulama seviyesinde yardımcı olacak sınıflar için AppUtil modülü açıldı ve ilgili sınıflar buraya taşındı
  • Base, Generic, Widget’lar için UI package oluşturuldu. Activity/ViewModel gibi UI sınıfıların Base’leri için Base package, run time esnasında backend üzerinden oluşturulan Activityler için Generic package, Widget ise Widget package altında toplandı.
  • Repo/UseCase gibi network sınıflarının Base’leri ve Common servislerin datalarının, imzalarının yer aldığı :Core:Data modülü açıldı

Bizi Bekleyen Zoruluklar Nelerdi? ⚒️

Bağımlıkları Koparma

  • Modülleri bir birine gereksiz yere göstermek istemediğimiz için sınıflar arasındaki bağımlıklıklar bizi oldukça zorladı
  • Bağımlılıkları silerek kodlar tekrar yazıldı
  • Tekrar yazılamayacak kodlar için Hilt DI avantajını kullanarak Interface üzerinden ilgili bağımlıklar çözümlendi

Conflict Yığını

İlgili dosya taşımaları, refactorleri yapılırken aynı zamanda uygulama içerisinde geliştirmeler de devam ediyordu. Bu yüzden sürekli olarak conflictler ile karşı karşıya kaldık.

Prod Ortam İle Eşitleme

Her release süreci sonrası, bu geliştirmeyi yaptığımız base branch’e release branchimiz ile sürekli olarak eşit tuttuk. Bu süreç içerisinde taşınan bir dosyada release üzerinde değişiklik yapılmış olabiliyordu. Bu gibi durumlarda manuel merge yapmak zorunda kaldık.

Test Süreçleri / Release Hazırlıkları

Kritik geliştirmelerin testlerini ilgili takımlardan istesek de, bu kadar kapsamlı bir geliştirmenin testini çalışan bir sistemde yapılması oldukça zor. Bu yüzden düzenli olarak ekip içerisinde Firebase App Dist üzerinden paket çıkarak yaptığımız çalışmaların testlerini canlı ortamda yaptık.

Yeni Modül Yapımız Nasıl Oldu? ➡️

  • Core: Core dizini altında alt yapı ile ilgili modülleri oluşturuldu. Bu modüllerin bağımlılıkları oldukça az tutularak paralel build alınması sağlandı.
  • Features: Bu dizin altında yer alan feature UI modülleri kendi Data modülüne ve ihtiyaç duyduğu core modüllere erişebilir. Legacy gibi ağır bağımlılıklar kopartıldı
  • Data: Her feature takımına ait data modülleri açıldı. Paralel build için Data’ların bir birini görmesi engellendi. İlgili Data modüller UseCase katmanından dolayı ihtiyacı olduğu Util, AppUtil gibi modüllere erişebilmektedir.
  • Legacy: Bu dizinde yer alan modüller, sadece common ve ihtiyaç duyduğu core modüllere erişmektedir.
  • Common: Proje genelinde kullandığımız image, color, style gibi dosyalar Asset modülü altında toplandı. Uygulama genelinde statik olarak tuttuğumuz Const değişkenler için Constant modülü açıldı ve tek yerden kullanımı sağlandı.

Yeni Modül Yapısı Bize Neler Kazandırdı? 📚

  • Build Süresi: Eski modül yapımızda özellikle mobile-core modülü altında bir değişiklik yaptığımızda (UI hariç neredeyse her sınıf bu modülde olduğu için değişiklik yapmak imkansızdı) ortalama build süresi 15–20 dk civarını bulmaktaydı. Yeni modül yapısına geçtiğimizde ise cache build süresi 2–3 dk sürelerine kadar düştüğünü gözlemledik.
  • Sorumluklukları Belirleme: Daha parçalı ve iş bazlı modüller olduğu için sorumlular atanabilir hale geldi
  • Bakım Maliyetlerinin Azalması: İş bazlı modüller oluşturdukça ve sorumlular belirlendikçe bakım maliyetlerinin oldukça azaldığı, paralel ilerlemenin kolaylaştığı gözlemlendi. Yeni trendlere daha hızlı adapte olmaya başladık.
  • Geliştirme Kolaylığı: Eski mimaride kodlar dağınık olduğu için, bir geliştirme yaparken birden fazla noktaya dokunmak gerekebiliyordu. Yeni mimaride ise ilgili işlem tek bir yerde yapıldığı için daha kolay geliştirme yapılabiliyor. Buna örnek vermek gerekirse; yeni bir theme istendiğinde eski mimaride renk kodları dağınık olduğu için daha uzun sürecekti. Ancak şu aşamada tüm renk kodları Asset modülünde toplandığı için tek bir yerde değişik ile hızlı bir şekilde geliştirme yapılabilmektedir.

Son Söz

Bağımlılıkların iç içe girdiği, Multi-Module mimarisine uygun olmayan bir mobil bankacılık uygulamasında canlı ortamdayken; Multi-Module mimarisini oluşturmak, ciddi emek harcayıp, bizim için büyük bir meydan okuma oldu. Ancak planlama ve güzel bir strateji ile aşılamayacak zorluk yoktur felsefesi ile başarıya ulaştık.

Multi-Module yapılarının getirdiği esneklik, build sürelerinin azalması, modül bazlı sorumlular çıkartabiliyor oluşumuz projemizin ömrünü daha da uzattı. Bu süreçten edindiğimiz deneyimler, gelecekteki projelerler için de ciddi kazanımlar olacaktır.

Sizler de büyük bir projede Multi-Module mimarisini kurgulamayı düşünüyorsanız, projenize uygun mimarilerden birini seçip yapmanızı öneririz. Bu süreç; yorucu, zorlayıcı belki de yıpratıcı olabilir ancak hedefe ulaştığınızda projenize uzun bir ömür veriyor olacaksınız

Okuduğunuz için teşekkürler. Bir sonraki yazımız olan Convention Plugin Migration in QNBAndroid’ da görüşmek üzere.

--

--