Geleneksel yazılım geliştirmede uzun yıllar boyunca standart yaklaşım monolitik mimari oldu. Kullanıcı arayüzü, iş kuralları (business logic), arka plan görevleri ve veritabanı erişim katmanının tek bir kod tabanında (codebase) bir arada bulunduğu bu yapı; küçük ve orta ölçekli projelerde hız ve basitlik sağlarken, proje büyüdükçe hantal, yönetimi zor ve kırılgan hale gelir.
Mikroservis mimarisi, bu devasa tek parça yapıyı parçalayarak geniş ölçekli sistemlerin getirdiği darboğazları aşmayı hedefler. Bir uygulamayı tek bir monolit olarak paketlemek yerine, her biri yalnızca belirli bir iş kabiliyetine (business capability) odaklanan, birbirinden bağımsız çalışabilen, dağıtılabilen ve ölçeklenebilen küçük servisler bütünü olarak tasarlama yaklaşımıdır.
1. Temel Kavramlar
Mikroservis dünyasına adım atarken sistemi ayakta tutan ve monolitik yapılardan ayıran ana yapıtaşları şunlardır:
Bounded Context (Sınırlı Bağlam)
Domain-Driven Design (DDD) metodolojisinden ödünç alınan bu kavram, bir servisin sorumluluk sınırlarını belirler. Örneğin bir e-ticaret platformunda Sipariş (Order), Katalog (Catalog), Ödeme (Payment) ve Kullanıcı (Identity) süreçlerinin sınırları birbirinden kesin olarak ayrılır. Her servis yalnızca kendi etki alanındaki kurallardan sorumludur.
Database-per-Service (Servis Başına Veritabanı)
Mikroservislerin en ayırt edici ve taviz verilmemesi gereken kurallarından biridir. Her servisin yalnızca kendine ait, izole bir veri tabanı olmalıdır. Bir servis, başka bir servisin veri tabanına doğrudan SQL sorgusu atamaz; veriye ihtiyaç duyuyorsa ilgili servisin sunduğu API veya kuyruk üzerinden talep etmek zorundadır.
API Gateway (API Ağ Geçidi)
Dış dünyadaki istemcilerin (web, mobil, üçüncü parti uygulamalar) onlarca farklı mikroservisin IP ve port adresini bilmesi yönetilemez bir kaos yaratır. API Gateway, tüm istemci isteklerinin karşılandığı tek giriş kapısıdır. İstekleri ilgili servislere yönlendirir (routing); kimlik doğrulama (authentication), rate limiting, SSL sonlandırma ve yanıt birleştirme (aggregation) gibi ortak yükleri üzerine alır.
Servisler Arası İletişim (Inter-Service Communication)
Servisler tek bir bellek havuzunda doğrudan fonksiyon çağıramadıkları için ağ üzerinden haberleşir:
- Senkron İletişim: İstemcinin yanıt beklediği doğrudan isteklerdir. Genellikle REST API (HTTP/JSON) veya yüksek performanslı iç iletişim için gRPC (HTTP/2 ve Protobuf) kullanılır.
- Asenkron İletişim: Bir servisin ürettiği olayı (event) sisteme duyurduğu ve diğer servislerin bunu tükettiği yapıdır. RabbitMQ, Apache Kafka veya bulut mesaj kuyrukları (AWS SNS/SQS, Azure Service Bus) aracılığıyla yürütülür.
Service Discovery & Registry
Büyüyen ve container ortamlarında dinamik olarak ayağa kalkıp kapanan servislerin anlık IP ve port adreslerini takip eden rehber mekanizmasıdır. Bir servis diğerine istek atarken bu kayıt defterine sorarak hedef servisin canlı adresini öğrenir (Consul, Eureka veya Kubernetes CoreDNS).
2. Mikroservislerin Olmazsa Olmazları
Mikroservis mimarisini sadece kodları farklı klasörlere veya container'lara bölmek sanmak en yaygın yanılgıdır. Bu mimarinin dağıtık yapısından doğan karmaşıklığı yönetebilmek için aşağıdaki disiplinler zorunludur:
1. Bağımsız Dağıtım (Independent Deployability)
Bir serviste yapılan bir hata düzeltmesi veya yeni özellik, diğer servislerin yeniden derlenmesini, test edilmesini veya yayına alınmasını gerektirmemelidir. Bir servisin deployment'ı diğerlerini durduruyorsa, elinizdeki yapı bir mikroservis değil, "dağıtık bir monolit"tir (distributed monolith).
2. Dağıtık Gözlemlenebilirlik (Distributed Tracing & Logging)
Monolitik sistemlerde tek bir log dosyası üzerinden hata takibi yapmak kolaydır. Ancak bir kullanıcı isteğinin 5 farklı servisi dolaştığı bir mikroservis yapısında nerede hata alındığını veya hangi servisin yavaşlığa yol açtığını bulmak merkezi araçlar olmadan imkansızdır:
- Distributed Tracing (OpenTelemetry, Jaeger): Gelen her isteğe bir
Correlation ID(veyaTrace ID) atanır ve bu ID servisler arasında taşınarak uçtan uca zincir çıkarılır. - Merkezi Loglama (ELK Stack, Loki, OpenSearch): Tüm servislerin logları tek bir havuzda toplanmalı ve Trace ID ile filtrelenebilmelidir.
3. Hata Toleransı ve Dayanıklılık (Resilience & Circuit Breaker)
Dağıtık sistemlerde ağ kopmaları, servis çökmeleri veya gecikmeler istisna değil, olağan durumlardır. Bir servisin yavaşlaması domino etkisiyle diğer servislerin thread havuzlarını tüketmemelidir.
- Circuit Breaker (Devre Kesici): Bir servis art arda hata veriyorsa, diğer servisler ona istek göndermeyi geçici olarak keser ve hızlı bir hata (veya fallback) döner.
- Retry & Timeout: Her uzaktan çağrıya mutlaka bir zaman aşımı süresi tanımlanmalı ve akıllı yeniden deneme (exponential backoff) politikaları uygulanmalıdır.
4. Eventual Consistency (Nihai Tutarlılık) ve SAGA Pattern
Veritabanları ayrıldığında, ilişkisel veritabanlarının sunduğu geleneksel ACID (özellikle dağıtık transaction) garantileri kaybolur. Birden fazla servisi ilgilendiren bir işlemde (örneğin: sipariş oluştur, stoğu düş, karttan ödeme al) distributed transaction yerine Eventual Consistency benimsenir. Süreç boyunca bir hata oluşursa işlemi geri almak için tersine işletilen adımlar bütünü olan Saga Pattern (Koreografi veya Orkestrasyon tabanlı) işletilir.
5. Otomasyon, Container ve CI/CD Zorunluluğu
Elle (manuel) sunucuya bağlanarak onlarca servisi yönetmek sürdürülemez. Her servis kendi Docker container imajı olarak paketlenmeli; derleme, test ve deploy süreçleri CI/CD pipeline'ları (GitHub Actions, GitLab CI vb.) ile tamamen otomatikleştirilmeli ve orkestrasyon için Kubernetes ya da benzeri bir container orkestratörü kullanılmalıdır.
3. Ne Zaman Tercih Edilmeli?
Mikroservis bir amaç değil, belirli bir büyüklüğün getirdiği sorunları çözmek için ödenen maliyetli bir araçtır.
- Monolitik Başlangıç: Ürün pazar uyumu (Product-Market Fit) henüz netleşmemiş, veri modelleri sürekli değişen erken aşama projelerde mikroservisle başlamak mimari felç getirir. Monolitik mimariyle başlamak ve sınırlar netleştikçe parçalamak (Monolith-First yaklaşımı) endüstri standardı bir kabuldür.
- Mikroservise Geçiş Eşiği: Farklı ekiplerin aynı kod tabanında birbirini engellemeye başladığı, sistemin sadece belirli bir parçasının (örneğin sadece raporlama veya video işleme modülünün) yoğun ölçeklenme gerektirdiği ve yüksek erişilebilirlik gereksinimlerinin monoliti zorladığı aşamada mikroservislere geçiş rasyonel bir karardır.