Eğer internette gezinirken bir şekilde bu yazıya denk geldiyseniz bilgi seviyenize göre 2 seçeneğiniz var. Eğer tecrübeli bir developer iseniz ve spring cloud ile mikroservisler konusunda bilginiz varsa github 'dan bu projeyi fork ile alabilir ve config repository 'yi indirerek projeyi bu yazıya göre ayağa kaldırmaya çalışabilirsiniz. Eğer tecrübeli bir developer değilseniz yazı dizisinin ilk yazısından başlamanız gerekiyor. Bu yazıda hazırladığım mikroservis projesini analiz edecek ve bazı farklı araçlardan ve alternatif yaklaşımlardan bahsedeceğim. Bu araçları bu yazıda tanıtacaktım fakat yazı çok uzayacaktı. Bu yüzden o konu başka yazıda olacak. Burada "ne yaptık", "neden yaptık", "doğru yaptık mı" ve "ne elde ettik veya kaybettik" gibi sorular soracağım. Bahsi geçen mikroservis mimarisi uygulamasının mimarisi aşağıdaki resimdeki gibi.
Bu uygulama ile mikroservis ile çalışan bir biletleme uygulaması oluşturmuş olduk. Omurga sistem olarak Gateway, Discovery Server ve Config Server kullandık. Küçük servislerimizi Eureka 'ya bağladık ve load balance yaptırdık. Gateway ile gelen istekleri Eureka 'ya path 'lere göre yönlendirdik ve erişilememe durumundaki routing 'ler ile resilience sağladık. Sonrasında küçük servisleri ve failover 'larını REST endpointleri ile tasarladık ve buralarda iş akışlarına dair kodlar yazmadık. Kullanıcılar, etkinlikler ve ödeme sistemi fonksiyonları için metodlar yazdık ve bunları küçük servislerde gruplandırdık.
Bu projeler kodlandıktan sonra bir tüketici MVC web uygulaması yazdık ve burada SAGA 'lar şeklinde iş akışlarını kodladık. Bu MVC uygulaması JWT header bilgileri ile request 'leri gateway 'e ileterek küçük servislere erişti. Ayrıca koordinasyon görevini üstlenerek servis çağrılarını ve response 'lara göre hataları da yönetti. Config Server ise spring boot konfigürasyonlarını aynı noktada (github) toplayabilmemize ve buradan dağıtabilmemize olanak sağladı. Kullandığım veritabanları MySql veritabanları idi fakat mikroservis açısından hayati bir önem arz etmiyordu. Bütün bu altyapının sponsorluğunu tabi ki spring cloud üstlendi :)
Bu uygulamaya domain driven design ile başlayıp bounded context 'ler tanımlayabilirdik. Ben kendi kararlarımı dikte ederek bu kısmı bir bakıma atlamış oldum. Bana sorarsanız bu sistemde kullanıcı, etkinlik ve ödeme işlemi bir bounded context olabilirdi çünkü bu kavramlar herkes tarafından ortak bir anlama sahip olabilir. Tabi bu düşüncem hatalı da olabilir. Yorumlarda bu sistem için domain driven design 'ı nasıl uygulayacağım konusunda sizin fikrinizi de duymayı isterim.
Burada gateway pattern kullanmış olduk ama konu mikroservisler olunca bu sadece bir isimden ibaret bence. Çünkü aynı zamanda SAGA ve circuit breaker desenlerini de kullandık. Eğer bir yazılımcıya bu web servisleri design pattern 'ler ile ifade ederseniz eminim herkes kafasında farklı bir şema çizecektir. Bu yüzden yazı dizisi boyunca tasarım desenlerinden bahsetmedim. Bu desenler konusunda bilgi sahibi olun tabi ki ama doğru deseni kullanmanız mikroservislerinizin başarılı bir şekilde yaşayıp yaşayamayacağını belirleyecektir. Yani NEDEN sorusu önemli aslında. Sebeplerimize (benim sebeplerime aslında) bir bakalım.
Aldığınız her karar bir sebebe dayandırılmış olmalı. Bu sebep de sadece "ben böyle biliyordum" veya "bana böyle dediler" gibi olmayan mantıksal veya pragmatic bir sebep olsa iyi olur tabi. Ben bütün sistemde teknolojiler ve araçlarla ilgili birkaç karar aldım tabi.
Bunlar hatırladığım kadarıyla yolculuk boyunca aldığım en önemli kararların sebepleri idi. Bu uygulamaya mikroservis kodlama deneyimim olmadan başladım ve bu yüzden süreçte bazı parçalar değişti. Ama sonuçta yukarıdaki resmin oluşmasını bu sebepler sağladı. Benim nedenlerim ve çıkarımlarım yanlış olabilir veya tartışılabilir. Bu sorunlara veya isterlere daha iyi çözümler de üretilebilir. Ama her ne olursa olsun neden sorusuna cevap verebilmeniz gerekiyor. Sonuçta bu sistemi kurgulayarak birşeyler elde etmeyi planlıyorsunuz. Bu da sıradaki başlığı getiriyor.
Aldığım kararlarla bazı kazanımlar elde ettiğimi düşünüyorum.
Eğer kendimi kandırmıyorsam sistemin avantajları bunlar olsa gerek. Burada ileri görüşlü olamayabilirim veya dar görüşlü olabilirim. Başlangıçta gereksinimlerimiz, kısıtlarımız ve hedeflerimiz vardı hatırlarsanız. Sadece hedefleri tutturmuş olmam olası bütün gereksinimleri düşünmüş olduğumu göstermez. Bu aynı zamanda doğru şeyi yaptığımızı veya sorunları doğru yoldan çözdüğümüzü de göstermez. Yorumlarda beni gömebilirsiniz :) Bu mimari ve uygulama hakkında dürüst olmam gerekirse dezavantajlarından da görebildiğim kadarıyla bahsetmem gerekir. Bu kısım mikroservis uygulamanızı işleme aldıktan sonra ortaya çıkar genelde.
Görünüşe göre dünyadaki en süper mikroservis mimarisi uygulamasını yazmışım. Tekrar söyleyeyim, burada ileriyi yeterince göremiyor veya geniş açı ile bakamıyor olabilirim. Veya taraflı bir şekilde sistemi değerlendiriyor da olabilirim. Bu yüzden bu durumda dışardan bakan bir göz size yeni bir bakış açısı kazandırabilir. Dahası "bunu doğru yaptım mı" sorusunun cevabını dürüst bir şekilde vermem mümkün değil. Ben kendi gereksinimlerimi çıkardım ve belli kriterlere ve hedeflere göre çözümler ürettim. Bana sorarsanız beklenen hedefleri tutturdum ve doğru yaptım derim. Ama mikroservisler soyut ve değişkenlik gösterebilen kavramlar olduğu için bu konuda genel geçer doğrular yerine ödünleşimler oluyor. Hala benim bir mikroservis uzmanı olduğuma inanmıyorsanız, haklısınız :) Benim de kafamda bazı sorular oluştu ve cevaplarını bulamadım.
Yorumlarda bu sorular hakkındaki düşüncelerinizi duymaktan mutlu olurum. Bu sorular biraz da "bu işi doğru yaptım mı" düşüncelerinden kaynaklanıyor.
Bu noktada teknik olarak hazırladığım mikroservis ile işim bitiyor. Uygulamayı bitirmiş oldum ve savunmamı da yaptım. Ama her zaman biraz hayalci veya filozof olma yatkınlığım bulunuyor. Bu yüzden 2 tane "peki ya.." sorusu ve çözümü de var kafamda. Bunlar kendi tecrübemle ürettiğim alternatif çözümler aynı zamanda.
İş gereksinimlerinin büyümesi ve karmaşıklaşması ihtimali her zaman bulunuyor tabi ki. Bu durum bence 2 soruna yol açardı. Uzun response süresi ve çok büyük controller veya servis sınıfları. Bu durumda yaptığım sistemi değiştirmezdim. Çünkü aksi takdirde küçük servisleri de iş gereksinimleri ile şişirmem gerekirdi. MVC uygulamasını SAGA 'lara göre molülerleştirmeye çalışabilirdim maven module yapısı ile. Bu en azından kodları birbirinden ayırabilirdi. Ayrıca tasarım desenleri de anlamlı kod yazabilmek adına yardımcı olacaktır. Çünkü aslında MVC uygulamasında bir visitor deseni uyguluyoruz.
Uzun response süresine bir çözüm bulamazdım sanırım. Çünkü ne yaparsam yapayım küçük servisler defalarca çağırılacak. Ama bu çağrımlar MVC uygulamasına fazla yük bindirirse MVC uygulamasını yedekleyip bunlar arasında load balance yapmayı düşünebilirdim. Bütün bunlar mesaj tabanlı yaklaşım ile servisler arası makarna ilişkiler kurmamak için.
Ama ne yaparsam yapayım sanırım koreografi tabanlı mikroservis mimarisi de bazı sorunlar için çözüm olabiliyor. Bir gün bunun için de bir örnek yapabilirim belki. Hali hazırda bir sorum daha var zaten.
Bu durumda gateway ve eureka 'ya ihtiyaç duymayabilirdim. Bildiğim kadarıyla sistemin tam ortasına network hub gibi bir message queue (kafka / rabbitmq / activemq) koyabiliyorum. Bütün servisleri de bir dinleyici olarak tasarlayıp mesaj kuyruklarına abone yapabiliyorum. MVC uygulaması bu durumda bir işlem başlatır ve mesaj iletir. Sonraki ilk servis mesajı alır ve kendi işini yapar. Sonrasında gelen payload yani veriye göre veya iş akışına göre veriyi ilgili kuyruğa iletir. Bu bir bakıma Chain of Responsibility ve Mediator tasarım deseninin karışımı.
Ama bütün iş akışlarını düşündüğümde böylesi çok fazla kuyruğa sebep olurdu gibi geliyor ve bu da bence mantıklı değil. Ayrıca çok fena distributed tracing aracı ihtiyacı olurdu, Sleuth + Zipkin veya başka bir tool gerekirdi. MVC uygulaması da bu kuyrukları dinleyen bir yapıya gelmek zorunda kalırdı ve belki de bir reaktif kütüphane kullanması gerekirdi. Koreografi yapısında işleyişi bu şekilde görüyorum.
Bu yolculuğun sonunda emin olduğum tek bir gerçek var. Bu sistemi oluşturan bütün kararlar, araçlar ve bileşen yapıları sorgulanabilir veya eleştirilebilir. Öğrenerek ve kodlayarak bir mikroservis çözümü üretmiş ve araçlarını tanımış oldum. Bu çözümlerde koordinatör veya mesaj tabanlı yaklaşım dışında başka bir seçenek göremedim. Bütün bu süreçten edindiğim ise:
Bu yazı ile kendi hedefimi tamamladım ve çalışan bir spring cloud mikroservis uygulamam olmuş oldu. Sıfır tecrübe ile benimle beraber buraya geldiyseniz kendinizi tebrik edin. Bu noktadan sonra mikroservislerin yönetimi gibi konular geliyor. Fark ettiyseniz bu sistemde bir değişiklik yapıldığında servisleri manuel olarak çalıştırmak ve deploy etmek zorundasınız. Bu yüzden "continuous integration" ve "continuous deployment" kavramları hayatımıza giriyor. Bu da sizi Devops denilen başka bir yolculuğa çıkarıyor. O konuda da bir gün kısa birşeyler yazabilirim. Bu yazı dizisi tamamlanmış oldu böylece. Kodu ve yazı dizisini yazma işi 2 ay sürdü. Bir sonraki yazıda görüşmek üzere :)
Bir yorum yazabilirsiniz