Bu Kadar Karmaşaya Gerçekten İhtiyacımız Var mı?
Yazılım dünyası böyle bir şey; sürekli yeni bir şeyler öğrenmek, sürekli güncel kalmak zorundasınız. Ama bazen bu hızın içinde, temel doğruları unuttuğumuzu hissediyorum. Özellikle son zamanlarda hangi geliştiriciyle konuşsam, hangi teknoloji bloguna baksam tek bir kelime duyuyorum: Mikroservisler.
Sanki mikroservis mimarisine geçmeyen projeler "eski", "hantal" veya "yetersiz"miş gibi bir algı oluştu. 2005'ten beri kod yazan, hem spagetti kodun en alasını görmüş hem de tertemiz mimariler kurmuş biri olarak şunu açıkça söyleyebilirim: Bu, kocaman bir yanılgı.
Bugün Pixel Lab® çatısı altında müşterilerim için çözümler üretirken, her zaman masaya yatırdığım ilk soru "Bunu en sade nasıl çözeriz?" oluyor. Ormanda kamp yaparken nasıl ki sırt çantanıza sadece hayati malzemeleri alıyorsanız, yazılım mimarisinde de ihtiyacınız olmayan yükleri sırtlanmamanız gerekir. Gelin, şu "Monolitik mi, Mikroservis mi?" tartışmasına, teknik terimlerin soğukluğundan sıyrılıp, biraz tecrübe, biraz da mantık çerçevesinden bakalım.
Monolitik Yapı: Eski Dost Düşman Olmaz
Genç yazılımcı arkadaşlarımın "Monolitik" kelimesini duyduklarında yüzlerini ekşittiklerini görüyorum bazen. Sanki dinozorlar çağından kalma bir fosilden bahsediyormuşuz gibi davranıyorlar. Oysa Monolitik yapı, doğru kurgulandığında inanılmaz güçlü bir "tek parça" sistemdir.
Monolitik mimariyi, benim doğa tutkumla bağdaştırırsam; tek bir gövdeden beslenen, kökleri ve dalları bir arada olan ulu bir çınar ağacına benzetebilirim. Her şey oradadır. Veritabanı erişimi, iş mantığı, arayüz kodları... Hepsi aynı projenin, aynı "solution"ın içindedir.
Pixel Lab®'da yeni bir projeye başlarken, eğer proje bir startup fikriyse veya orta ölçekli bir kurumsal çözümse, elim neredeyse her zaman Monolitik yapıya gidiyor. Neden mi?
- Hız ve Basitlik: Projeyi ayağa kaldırmak dakikalar sürer. Tek bir repo, tek bir derleme (build) süreci. "Hadi yayına alalım" dediğinizde dosyaları sunucuya atarsınız ve biter.
- Hata Ayıklama (Debugging) Kolaylığı: Bir hata mı var? Breakpoint'i koyarsınız, kodu satır satır takip edersiniz. Servisler arası ağ trafiğini izlemenize, "Acaba hata A servisinde mi yoksa B servisine giden pakette mi?" diye düşünmenize gerek kalmaz. Her şey elinizin altındadır.
- Performans: İnanın bana, fonksiyon çağrısı (function call), ağ çağrısından (network call) her zaman daha hızlıdır. Monolitik yapıda modüller birbirleriyle hafıza üzerinden konuşur, milisaniyelerle değil, nanosaniyelerle ölçülen hızlardan bahsediyoruz.
Tabii ki "Spagetti Kod" ile "Monolitik Mimari"yi karıştırmamak lazım. İyi bir monolit, modülerdir. Yani kodlarınız yine klasörlere, katmanlara (layer) ayrılmıştır ama fiziksel olarak tek bir bütün halindedir.
Mikroservisler: Büyüleyici Ama Tehlikeli Bir Orman
Şimdi gelelim madalyonun diğer yüzüne. Mikroservisler... Kağıt üzerinde harika duruyor, değil mi? "Sepet servisi çökerse, ödeme servisi çalışmaya devam etsin." Kulağa çok hoş geliyor. Ama pratikte işler bu kadar romantik yürümüyor.
Ben mikroservisleri, her biri kendi ekosistemine sahip, aralarında nehirlerin aktığı adacıklara benzetiyorum. Bir adadan diğerine malzeme (veri) göndermek için köprüler kurmanız, sandallar (API call) kullanmanız gerekiyor. Ve doğada olduğu gibi, hava bozduğunda (network latency, timeout) o sandallar batabiliyor.
Bir projeyi mikroservis mimarisine geçirdiğinizde, aslında bir "yazılım problemi"ni çözmüyorsunuz; bir "dağıtık sistem problemi"ni kucağınıza alıyorsunuz. Ve inanın bana, dağıtık sistemler yönetmesi en zor yapılardır. Karşılaşacağınız zorlukları şöyle özetleyeyim:
1. Veri Tutarlılığı (Data Consistency) Kabusu
Monolitik yapıda "Transaction" dediğimiz harika bir şey vardır. Bir işlem sırasında hata olursa, her şeyi geri alırsınız (Rollback). Veritabanınız tertemiz kalır. Mikroservislerde ise veriniz farklı veritabanlarına dağılmıştır. Sipariş servisinde stok düştü ama ödeme servisinde karttan para çekilemedi... Ne olacak şimdi? "Distributed Transaction" yönetmek, kod yazmaktan çok daha fazla efor ve uzmanlık gerektirir. Saga patternler, event buslar havada uçuşmaya başlar.
2. Operasyonel Maliyet
Eskiden tek bir sunucuyu izliyordunuz (monitoring). Şimdi 20 farklı servisi, onların birbirleriyle olan iletişimini, her birinin loglarını, her birinin CPU kullanımını izlemeniz gerekiyor. DevOps kültürü ve araçları (Kubernetes, Docker Swarm vb.) olmadan bu işe girerseniz, o projenin altında kalırsınız. Ben tek başıma veya küçük bir ekiple çalışırken, enerjimi altyapıyı ayakta tutmaya değil, müşterime değer üretmeye harcamak isterim.
3. Gecikme (Latency)
Daha önce hafıza içinde saniyenin milyonda biri hızında gerçekleşen iletişim, artık HTTP veya gRPC üzerinden ağ kablolarında dolaşıyor. Her servis çağrısı bir maliyet, bir bekleme süresi demek. Zincirleme birbirini çağıran 5 servisiniz varsa, kullanıcı ekran başında dönen o yükleme ikonuna bakarak yaşlanabilir.
Peki, Ne Zaman Parçalamalıyız?
"Buğrahan, sen de mikroservisleri iyice gömdün, hiç mi kullanmayalım?" dediğinizi duyar gibiyim. Elbette hayır. Doğru yerde kullanılan mikroservis mimarisi hayat kurtarır. Ama anahtar kelime: Doğru Yer.
Bir yapıyı parçalamaya karar vermem için şu sinyalleri almam gerekir:
- Ekip Ölçeği: Eğer projenizde 50-100 geliştirici çalışıyorsa ve herkes aynı codebase'e (kod tabanına) commit atarken birbirini eziyorsa (merge conflict cehennemi), o zaman ayrılma vakti gelmiştir. Her ekip kendi servisinden sorumlu olur, teknolojisini kendi seçer.
- Farklı Ölçeklenme İhtiyacı: Diyelim ki bir e-ticaret siteniz var. "Ürün Görüntüleme" modülü saniyede 10.000 istek alırken, "Fatura Oluşturma" modülü günde sadece 100 istek alıyor. Monolitik yapıda tüm sistemi büyütmek zorundasınız. Mikroservislerde ise sadece "Ürün Görüntüleme" servisine daha fazla sunucu gücü verip, diğerlerini küçük tutabilirsiniz. Kaynak tasarrufu budur.
- Teknoloji Bağımsızlığı: Sistemin bir parçasında Yapay Zeka kullanacaksınız ve Python bu iş için harika. Ama ana sisteminiz .NET veya Java. İşte bu noktada o AI modülünü ayrı bir mikroservis olarak yazıp ana sisteme bağlamak en mantıklı yoldur.
Benim Tercihim: Modüler Monolit (Modular Monolith)
Yazılım dünyasında siyah ve beyaz yoktur, griler vardır. Ben projelerimde genellikle "Modüler Monolit" yaklaşımını benimsiyorum. Bu, benim için "altın oran" gibi bir şey.
Nedir bu? Kodları fiziksel olarak ayırmadan (hala tek bir deployable unit), mantıksal olarak çok sıkı sınırlarla ayırmaktır. Modüller birbirlerinin veritabanı tablolarına asla doğrudan erişmez, sadece tanımlı arayüzler (interface) üzerinden konuşur. Yani kodunuzu öyle bir yazarsınız ki, sanki mikroservismiş gibi disiplinli olur ama monolitik yapının dağıtım ve yönetim kolaylığını korur.
Günün birinde proje patlama yaparsa ve bir modülü ayırmanız gerekirse, o modülü kesip ayrı bir servise dönüştürmek, iyi tasarlanmış bir Modüler Monolit'te sadece birkaç saatlik iştir. Ama baştan mikroservis ile başlayıp sonra "bu çok karmaşık oldu" diyerek birleştirmeye çalışmak... İşte o imkansıza yakın bir eziyettir.
Doğadan Aldığım İlham
Hafta sonları bisikletimle ormana daldığımda veya kamp ateşini izlediğimde doğanın sadeliğine hayran kalıyorum. Doğa, karmaşık problemleri en basit yoldan çözer. Enerji tasarrufu yapar, gereksiz hiçbir uzvu barındırmaz. Bir ağacın dalı, gövdeden ayrılmaya ancak ve ancak kendi başına bir fidan olup toprağa düşmesi gerektiğinde karar verir. O zamana kadar bütüne hizmet eder.
Biz yazılımcılar da bazen egomuza yenik düşüp, sırf CV'mizde güzel dursun diye, sırf "Netflix de böyle yapıyor" diye projeleri over-engineer (aşırı mühendislik) ediyoruz. Oysa Netflix'in problemleriyle, Manavgat'taki bir otel zincirinin rezervasyon sisteminin problemleri aynı değil.
Yol Haritası: Nasıl Karar Vermelisiniz?
Bir projeye başlarken veya mevcut bir sistemi dönüştürürken kendinize şu soruları sorun:
1. Bu projeyi ne kadar sürede canlıya almam gerekiyor?
Eğer aceleniz varsa, MVP (Minimum Viable Product) aşamasındaysanız, kesinlikle Monolitik başlayın. Hız, piyasada tutunmanın ilk kuralıdır.
2. Ekibim dağıtık sistemleri yönetecek yetkinlikte mi?
Docker, Kubernetes, Service Mesh, Distributed Tracing... Bu terimler ekibinize yabancıysa, mikroservis macerası hüsranla bitebilir. Öğrenme eğrisi çok diktir.
3. Problemim gerçekten "ölçeklenme" (scaling) mi, yoksa "kötü kod" mu?
Çoğu zaman performans sorunları mimariden değil, kötü yazılmış SQL sorgularından veya yanlış algoritmalardan kaynaklanır. Kötü kodu mikroservislere bölmek, sadece dağınık kötü kodlarınız olmasını sağlar.
Sadelik En Yüksek Gelişmişlik Düzeyidir
Leonardo da Vinci'nin o meşhur sözünü çok severim: "Sadelik, en yüksek gelişmişlik düzeyidir." Kod yazarken de, mimari kurarken de, hatta hayatı yaşarken de bu benim pusulam.
Pixel Lab®'da amacım, müşteriye "Bakın ne kadar karmaşık bir sistem kurduk" demek değil; "Bakın, işleriniz ne kadar akıcı ve sorunsuz yürüyor" dedirtmektir. Eğer monolitik bir yapı işi sorunsuz, hızlı ve güvenli bir şekilde hallediyorsa, o proje için dünyanın en iyi mimarisi odur.
Mikroservisler bir amaç değil, bir araçtır. Çekiç elimizde diye her şeyi çivi olarak görmeyelim. Bazen ihtiyacımız olan sadece sağlam bir tornavidadır. Projenizi, ekibinizi ve hedeflerinizi iyi analiz edin. Ve en önemlisi, sistemlerinizi yönetilebilir, hayatınızı da sade tutun. Çünkü günün sonunda bilgisayarı kapattığımızda, o sessizliğin ve huzurun tadını çıkarabilmek için zihnimizin berrak kalması gerekiyor.
Bir sonraki yazıda veya belki bir kamp ateşi başında görüşmek üzere. Kodlarınız temiz, rotanız açık olsun.
Yorumlar
Henüz yorum yapılmamış. İlk yorumu siz yazın!
Yorum Yaz