İçeriğe Atla
Mustafa Erbay
Yaşam · 12 dk okuma · görüntülenme Read in English

Monolith mi, Modüler Mimari mi? Indie Hacker'ın Geçiş Yolculuğu

Monolith ve modüler mimari arasındaki farkları, indie hackerlar için geçişin zorluklarını ve pratik çözümleri kendi deneyimlerimle anlatıyorum.

100%

Monolith’ten Modüler Dünyaya: Neden Bu Yolculuk?

Bir indie hacker olarak kendi projelerimi geliştirirken karşıma çıkan en temel mimari kararlardan biri, monolithic bir yapı ile mi ilerleyeceğim, yoksa baştan modüler bir yapı mı kuracağım sorusu oldu. Kendi finansal hesaplayıcılarımı geliştirirken, bir e-ticaret sitesinin backend’ini yazarken veya mobil uygulamalar tasarlarken bu ikilem hep benimleydi. Genellikle projelerimin ilk aşamalarında hız ve basitlik adına monolithic yapıyı tercih ettim. Ancak zamanla, özellikle kullanıcı sayısı arttığında ve yeni özellikler eklemek gerektiğinde bu yapının sınırlarını görmeye başladım. Bu yazıda, monolithic yapıdan modüler bir mimariye geçişin benim için neden bir zorunluluk haline geldiğini, bu geçiş sırasında karşılaştığım somut zorlukları ve bu zorlukların üstesinden nasıl geldiğimi anlatacağım.

Monolithic yapının cazibesi, projenin başlangıcında sunduğu hız ve basitlikte yatıyor. Tüm kodun tek bir yerde olması, geliştirme sürecini başlangıçta oldukça kolaylaştırıyor. Tek bir codebase üzerinde çalışmak, bağımlılıkları yönetmeyi basitleştiriyor ve ilk deploy’ları hızlandırıyor. Ancak bu basitlik, projenin büyümesiyle birlikte bir handikaba dönüşebiliyor. Kod tabanı büyüdükçe okunması, anlaşılması ve üzerinde değişiklik yapılması zorlaşıyor. Yeni ekip üyelerinin projeye adapte olması da cabası. Kendi deneyimlerimde, özellikle bir üretim ERP’si üzerinde çalışırken, monolithic yapının getirdiği bu karmaşıklığın yeni özellik geliştirme sürelerini nasıl uzattığını defalarca gördüm.

Monolithic Yapının Sınırları: Gerçek Dünya Senaryoları

Kendi projelerimde, özellikle kullanıcı sayısı belirgin şekilde arttığında monolithic yapının sınırlarını hissetmeye başladım. Örneğin, kendi finansal hesaplayıcılarımı barındıran sitemde, aynı anda birden fazla kullanıcının yoğun işlem yaptığı anlarda sistem yanıt süresi belirgin şekilde artıyordu. Bu durumun temel nedeni, tüm iş mantığının ve veritabanı erişiminin tek bir noktada toplanmış olmasıydı. Yoğun bir hesaplama isteği geldiğinde, diğer kullanıcıların istekleri de bu yoğunluktan etkileniyordu. Bu, özellikle “gerçek zamanlı” hissi vermesi gereken uygulamalar için kabul edilemez bir durumdu.

Bir diğer örnek, Android’de geliştirdiğim spam engelleyici uygulamam. Başlangıçta tüm mantık tek bir Activity içinde toplanmıştı. Ancak zamanla, arka planda çalışan servislerin ve kullanıcı arayüzü güncellemelerinin senkronizasyonu zorlaştı. Özellikle yoğun bildirim trafiği olduğunda, uygulama belleği (memory) aşırı kullanılabiliyor ve performans sorunları yaşanıyordu. Bu tür durumlarda, monolithic yapının “her şeyin birbirine bağlı olması” avantajı, aynı zamanda en büyük dezavantajına dönüşüyordu. Bir modüldeki sorun, tüm uygulamayı etkileyebiliyordu.

Modüler Mimarinin Cazibesi: Indie Hacker İçin Neden Önemli?

Modüler mimari, bir uygulamayı bağımsız, değiştirilebilir ve yeniden kullanılabilir bileşenlere ayırma prensibine dayanır. Her bir modül kendi iş mantığına, veritabanına ve hatta belki de kendi teknoloji yığınına sahip olabilir. Bu yaklaşım, özellikle benim gibi tek başıma veya küçük bir ekiple çalışan indie hacker’lar için devrim niteliğinde olabilir. Çünkü modülerlik, geliştirme sürecinde büyük bir esneklik ve verimlilik sağlar.

Örneğin, kendi projemde bir kullanıcı kimlik doğrulama servisini (authentication service) ayrı bir modül haline getirdiğimi düşünelim. Bu modülü, FastAPI ve PostgreSQL kullanarak geliştirebilirim. Daha sonra, ana uygulamamın farklı bölümleri (örneğin, finansal hesaplayıcılar veya raporlama modülü) bu kimlik doğrulama servisine API çağrıları yaparak erişebilir. Eğer ileride kimlik doğrulama sistemini daha güvenli veya daha performanslı bir teknolojiyle değiştirmek istersem, sadece bu modülü güncellemäm yeterli olur. Ana uygulamamın geri kalanı bu değişiklikten etkilenmez. Bu, uzun vadede bakım maliyetlerini düşürür ve inovasyon hızını artırır.

Geçişin Zorlukları: Beton Duvarlar ve Gizli Tuzaklar

Monolithic yapıdan modüler bir mimariye geçiş, teoride kulağa hoş gelse de pratikte önemli zorluklar barındırır. Bu zorluklar, özellikle benim gibi tek başına çalışan bir geliştirici için daha da belirginleşir. En büyük zorluklardan biri, mevcut monolithic uygulamanın nasıl bölüneceğine karar vermek. Hangi iş mantığı hangi modüle ait olmalı? Veritabanı erişimi nasıl yönetilmeli? Modüller arasındaki iletişim nasıl sağlanmalı? Bu soruların cevapları, projenin mimarisini baştan sona yeniden tasarlamayı gerektirebilir.

Bir diğer büyük zorluk ise veri tutarlılığını sağlamak. Monolithic yapıda, bir işlemdeki tüm adımlar aynı veritabanı üzerinde atomik bir şekilde gerçekleştirilebilir. Ancak modüler bir mimaride, farklı modüller farklı veritabanlarını kullanabilir veya aynı veritabanını paylaşabilirler. Bu durumda, bir modüldeki işlemin başarılı olup diğer modüldeki işlemin başarısız olması gibi durumlar ortaya çıktığında veri tutarsızlığı yaşanabilir. Bu tür “eventual consistency” senaryolarını yönetmek, karmaşık mekanizmalar gerektirir. Kendi projelerimde, bu tutarsızlıkları yönetmek için “transaction outbox” pattern’ini ve mesaj kuyruklarını (message queues) kullanmayı öğrendim.

Pragmatik Yaklaşımlar: Adım Adım Modülerleşme

Bu zorlukların üstesinden gelmek için “her şeyi bir anda değiştirme” tuzağına düşmemek gerekiyor. Benim tercihim, “strangler fig pattern” gibi kademeli geçiş stratejileri oldu. Bu yaklaşımda, mevcut monolithic uygulamanın etrafına yeni modüler servisler inşa edilir ve zamanla trafik bu yeni servislere yönlendirilir. Bu sayede, projenin mevcut işleyişini bozmadan, kontrollü bir şekilde modülerleşme sağlanabilir.

Örneğin, bir e-ticaret sitesindeki ürün yönetimini monolithic yapıdan ayırmak istediğimi varsayalım. İlk adım olarak, ürün bilgilerini yöneten ayrı bir “Product Service” oluşturabilirim. Bu servis, kendi veritabanına sahip olabilir ve RESTful API aracılığıyla diğer servislere hizmet verebilir. Monolithic uygulamamdaki ürünle ilgili tüm istekler, artık bu yeni servise yönlendirilir. Bu geçiş sırasında, eski monolithic yapıdaki ürün verilerini yeni servisin veritabanına taşımak için bir veri senkronizasyon mekanizması kurmam gerekebilir. Bu, ilk etapta karmaşık görünse de, uzun vadede ürün yönetimi modülünün bağımsızlaşmasını sağlar.

Teknik Detaylar: Kod Örnekleri ve Yapılandırmalar

Modüler mimaride servisler arası iletişim genellikle API’lar aracılığıyla gerçekleşir. Benim tercihim genellikle RESTful API’lar oldu, ancak daha karmaşık sistemlerde gRPC veya mesaj kuyrukları (Kafka, RabbitMQ gibi) da kullanılabilir. Örneğin, bir “Order Service” (Sipariş Servisi) ve bir “Inventory Service” (Stok Servisi) düşünelim. Bir sipariş oluşturulduğunda, Order Service, Inventory Service’e bir API çağrısı yaparak stok durumunu kontrol etmeli ve stok miktarını güncellemelidir.

# order_service/api.py (Basit bir FastAPI örneği)
from fastapi import FastAPI, HTTPException
import requests

app = FastAPI()

INVENTORY_SERVICE_URL = "http://inventory-service:8000" # Docker Compose'da servis adı

@app.post("/orders/")
async def create_order(order_data: dict):
    product_id = order_data.get("product_id")
    quantity = order_data.get("quantity")

    if not product_id or not quantity:
        raise HTTPException(status_code=400, detail="Product ID and quantity are required.")

    try:
        # Stok servisinden stok kontrolü ve güncelleme
        response = requests.post(f"{INVENTORY_SERVICE_URL}/inventory/deduct", json={
            "product_id": product_id,
            "quantity": quantity
        })
        response.raise_for_status() # Hata durumunda istisna fırlat

        # Stok başarılı bir şekilde düşüldü, siparişi oluştur
        # ... (sipariş veritabanına kaydedilir) ...
        return {"message": "Order created successfully", "order_id": "ORD12345"}

    except requests.exceptions.RequestException as e:
        raise HTTPException(status_code=500, detail=f"Inventory service error: {e}")

Bu örnekte, Order Service, Inventory Service ile haberleşiyor. Eğer Inventory Service bir hata dönerse (örneğin, stok yoksa), Order Service de bir hata mesajı döndürür. Bu, basit bir senkron iletişim örneğidir. Ancak daha sağlam bir sistemde, stok düşme işleminin başarısız olması durumunda siparişin iptal edilmesi veya bir insan tarafından incelenmesi gibi ek mantıklar gerekebilir. Bu tür durumlar için “saga pattern” gibi daha gelişmiş senaryolar devreye girer.

Veritabanı Stratejileri: Paylaşılan mı, Ayrık mı?

Modüler mimaride veritabanı stratejisi de kritik bir karardır. İki ana yaklaşım vardır:

  1. Paylaşılan Veritabanı: Tüm modüller aynı veritabanını kullanır, ancak farklı tabloları veya şemaları kullanabilirler. Bu, başlangıçta daha basit olabilir ancak modüller arasındaki bağımlılığı artırır. Bir modülün veritabanı şemasında yapacağı bir değişiklik, diğer modülleri de etkileyebilir.
  2. Ayrık Veritabanları: Her modül kendi veritabanına sahip olur. Bu, modüllerin tam bağımsızlığını sağlar ve teknoloji seçimi konusunda daha fazla esneklik sunar. Ancak, farklı veritabanları arasındaki veri tutarlılığını sağlamak daha karmaşık hale gelir.

Pratikte, projelerin başlangıcında paylaşılan bir PostgreSQL veritabanı ile başlayıp, zamanla daha kritik veya bağımsız hale gelen modülleri ayrı veritabanlarına taşımak iyi bir strateji oldu. Örneğin, kullanıcı veritabanı başlangıçta ana veritabanında olabilirken, daha sonra ayrı bir “User DB” olarak yapılandırılabilir. Bu geçiş, pg_dump ve pg_restore gibi araçlarla veya daha gelişmiş replikasyon yöntemleriyle yapılabilir.

Gerçek Dünya Uygulamaları ve Öğrenilen Dersler

Kendi “yan ürünüm” olan finansal hesaplayıcılar platformunda, monolithic yapıdan modüler bir yapıya geçiş hatırı sayılır bir zaman aldı. Başlangıçta, kullanıcı yönetimi ve temel hesaplama motorunu ayırdım. Bu, yanıt sürelerinde belirgin bir iyileşme sağladı. Daha sonra, farklı finansal enstrümanlar için (hisse senedi, kripto, emlak) ayrı modüller oluşturdum. Bu modüllerin her biri, kendi hesaplama algoritmalarını ve veri kaynaklarını yönetiyor.

Bu geçiş sırasında en çok zorlandığım nokta, “N+1 query problem” benzeri durumların modüller arası ilişkilerde de ortaya çıkabilmesiydi. Örneğin, bir kullanıcının tüm hesaplarını listelerken, her hesap için ayrı ayrı veri çekmek yerine, toplu sorgular yapmayı öğrenmem gerekti. Bu tür performans optimizasyonları, modüler mimaride bile kritik önem taşıyor.

Modüler mimariye geçiş, bir indie hacker için uzun vadede daha sürdürülebilir, ölçeklenebilir ve yönetilebilir projeler geliştirmesini sağlar. Bu yolculuk zorlu olsa da, sunduğu esneklik ve verimlilik, bu zorluklara değdiğini gösteriyor. Kendi projelerimde bu geçişi tamamladıkça, yeni fikirleri hayata geçirmek ve mevcut sistemleri iyileştirmek çok daha kolay hale geldi.

Paylaş:

Bu yazı faydalı oldu mu?

Yükleniyor...

Bu yazı nasıldı?

Sıkça Sorulanlar

Bu makale ile ilgili okurların sorduğu yaygın sorular.

Monolith'ten modüler mimariye geçişte karşılaşılan en büyük zorluk nedir?
Benim deneyimime göre, monolith'ten modüler mimariye geçişte karşılaşılan en büyük zorluk, kod tabanının büyümesiyle birlikte artan karmaşıklıktır. Bu, yeni özellik geliştirme sürelerini uzatabilir ve yeni ekip üyelerinin projeye adapte olmasını zorlaştırabilir. Ben, bu zorluğu aşmak için küçük adımlarla ilerleyerek ve her bir modülü ayrı ayrı geliştirerek karşıladım.
Modüler mimariye geçiş için hangi araçları kullanmalıyım?
Modüler mimariye geçiş için kullanılan araçlar, projenin büyüklüğü ve karmaşıklığına göre değişebilir. Ben, Docker ve Kubernetes gibi konteynırlaştırma araçlarını kullanarak modüler mimariyi daha kolay yönetebildim. Ayrıca, API gateway'leri ve mesaj kuyrukları gibi araçlar da modüler mimariyi desteklemek için faydalı olabilir.
Monolith'ten modüler mimariye geçişin avantajları nelerdir?
Monolith'ten modüler mimariye geçişin avantajları, artan ölçeklenebilirlik, daha hızlı geliştirme süreleri ve daha kolay bakım gibi faydaları içerir. Ben, modüler mimariye geçtikten sonra, projemin daha hızlı ve esnek bir şekilde büyümesini sağladım. Ayrıca, yeni özellikler eklemeyi ve hataları解决 etmeyi daha kolay buldum.
Modüler mimariye geçişte hata oluşursa ne yapmalıyım?
Modüler mimariye geçişte hata oluşursa, ilk olarak hata kaynağını belirlemek önemlidir. Ben, bu durumda, her bir modülün ayrı ayrı test edilmesini ve hata kaynağının belirlenmesini sağladım. Ardından, hata kaynağına göre gerekli düzeltmeleri yaparak ve sistemin tekrar test edilerek, hataların çözülmesini sağladım. Ayrıca, bir hata yönetimi planı oluşturmak da modüler mimariye geçişte faydalı olabilir.
ME

Mustafa Erbay

Sistem Mimarisi · Network Uzmanı · Altyapı, Güvenlik ve Yazılım

2006'dan bu yana sistem mimarisi, network, sunucu altyapıları, büyük yapıların kurulumu, yazılım ve sistem güvenliği ekseninde çalışıyorum. Bu blogda sahada karşılığı olan teknik deneyimlerimi paylaşıyorum.

Kişisel Notlar

Bu notlar sadece sizde saklanır. Tarayıcınızda yerel olarak tutulur.

Hazır 0 karakter

Yorumlar

Sunucu Taraflı AI Moderasyon

Yorumlar sunucuda yapay zeka ile denetlenir ve kalıcı olarak saklanır.

?
0/2000

Sunucu taraflı AI denetim

✉️ Ücretsiz · Spam yok · İstediğin an çık

Yeni yazılardan haberdar olun

Yeni içerikler ve teknik notlar e-postanıza gelsin.

  • 📌
    Haftanın en iyisi Sadece okumaya değer tek yazı
  • 🔧
    Alet çantası Bu hafta kullandığım araçlar
  • 🧠
    Perde arkası Blog'a girmeyen notlar

Spam yapmıyoruz. İstediğiniz zaman ayrılabilirsiniz. · Sadece Umami (self-hosted, Google yok) ile takip.

Okuma İstatistikleriniz

0

Yazı Okundu

0dk

Okuma Süresi

0

Gün Serisi

-

Favori Kategori

İlgili Yazılar