MİCROSERVİCE NEDİR? JAVA İLE SERVİS TASARIMI, İLETİŞİM VE HATA TOLERANSI
Bir uygulama büyüdükçe, “tek parça” bir kod tabanının yönetimi zorlaşır: ekipler birbirinin alanına basar, deploy süreleri uzar, küçük bir değişiklik tüm sistemi riske atar. Microservice yaklaşımı bu noktada, uygulamayı bağımsız evrilebilen servisler halinde tasarlayarak ölçeklenebilirliği ve çevikliği artırmayı hedefler.
Ancak microservice sadece “çok sayıda küçük servis” demek değildir. Yanlış sınırlar, dengesiz iletişim tercihleri veya zayıf hata toleransı; monolitin problemlerini büyüterek geri getirebilir. Bu nedenle microservice mimarisini Java ekosistemiyle birlikte; tasarım, iletişim ve dayanıklılık (resilience) perspektifinden ele almak en doğru yaklaşımdır.
Bu rehberde microservice mimarisini tanımlayacak, servis sınırlarını nasıl çizeceğinizi, servislerin nasıl haberleşeceğini ve kaçınılmaz arızalara karşı sistemin nasıl ayakta kalacağını adım adım inceleyeceğiz. Hedefimiz: yalnızca teoriyi değil, gerçekçi örneklerle uygulanabilir bir yol haritasını da sunmak.
Microservice Nedir? Temel Kavramlar ve Primary Keyword
Primary keyword: “microservice nedir” sorusuna pratik bir cevap vermek gerekirse: Microservice, bir uygulamanın iş yeteneklerine göre ayrılmış, bağımsız deploy edilebilen, kendi yaşam döngüsü olan servislerden oluşan mimari yaklaşımdır. Her servis belirli bir iş kabiliyetini sahiplenir; kendi verisini yönetebilir ve diğer servislerle iyi tanımlı arayüzler üzerinden iletişim kurar.
Microservice mimarisinde amaç, organizasyonel ve teknik bağımsızlığı artırmaktır. Servislerin ayrı ayrı sürümlenmesi, farklı hızlarda geliştirilmesi ve farklı ölçeklendirme ihtiyaçlarına göre büyütülmesi mümkündür. Bu sayede hem ürün geliştirme döngüsü hızlanır hem de sistemin bazı kısımları yoğun yük altında ölçeklenirken diğer kısımlar gereksiz kaynak tüketmez.
Monolit ile Microservice Arasındaki Fark
Monolitik mimaride tüm bileşenler tek uygulama içinde paketlenir. Bu yaklaşım başlangıçta hızlıdır; ancak zamanla bağımlılık ağı büyür. Microservice ise bu bağımlılıkları sınırlar. Yine de şu gerçek unutulmamalı: Microservice, dağıtık sistem karmaşıklığını beraberinde getirir. Ağ gecikmesi, kısmi hata (partial failure), veri tutarlılığı ve gözlemlenebilirlik gibi konular doğrudan mimarinin merkezine oturur.
Servislerin Bağımsızlığı Ne Demek?
Bağımsızlık; yalnızca ayrı deploy edebilmek değil, aynı zamanda net sorumluluk sınırları, düşük sıkı bağlılık ve kontrollü entegrasyon demektir. Bir servis değiştiğinde tüm ekosistemin kilitlenmemesi için geri uyumluluk ve sözleşme yönetimi disiplinleri devreye girer.

Servis Sınırlarını Çizmek: Domain, Bounded Context ve Sorumluluk
Başarılı bir microservice dönüşümünün en kritik noktası servis sınırlarıdır. En sık yapılan hata; teknik katmanlara göre (user-service, db-service gibi) bölmek ya da “mevcut paket yapısını servis yapmak”tır. Bunun yerine iş yetenekleri (business capability) ve domain diline göre ilerlemek gerekir.
DDD ve Bounded Context ile Tasarım
Domain Driven Design (DDD), servis sınırlarını belirlerken güçlü bir rehberdir. Bounded context; aynı terimlerin aynı anlamı taşıdığı, tutarlı bir model alanıdır. Microservice dünyasında her bounded context çoğu zaman bir servise karşılık gelir. Bu yaklaşım, “her şeyi herkes bilir” probleminden kaçınmanızı sağlar.
Veri Sahipliği ve “Paylaşılan DB” Tuzağı
Microservice mimarisinde servislerin kendi verisine sahip olması önerilir. Paylaşılan veritabanı, servisleri yeniden monolitik bağımlılığa iter. Bazı durumlarda raporlama veya legacy gereksinimler nedeniyle geçici hibrit çözümler görülebilir; fakat hedef, her servisin verisini yönetmesi ve dışarıya sadece API veya event ile bilgi vermesidir.
Java ile Microservice Ekosistemi: Spring Boot, Quarkus ve Seçim Kriterleri
Java ekosistemi microservice için olgun bir altyapı sunar. En yaygın yaklaşım Spring Boot ile hızlı servis geliştirmektir; Quarkus ve Micronaut gibi seçenekler de özellikle düşük bellek tüketimi ve hızlı başlangıç süresi hedeflerinde öne çıkar.
Framework Seçerken Nelere Bakmalı?
- Takımın mevcut uzmanlığı ve öğrenme maliyeti
- Startup süresi ve kaynak tüketimi (özellikle container yoğun ortamlarda)
- Gözlemlenebilirlik entegrasyonları (metrics, tracing, logging)
- Ekosistem olgunluğu ve dokümantasyon kalitesi
- Test stratejileri ve yerel geliştirme deneyimi
Burada tek “doğru” yoktur; ancak üretim gerçeklerinde, operasyonel ihtiyaçlar çoğu zaman seçim kriterlerinin başına yerleşir. Sadece geliştirme hızına odaklanıp işletim tarafını ihmal etmek, sürdürülebilirliği düşürür.
Servisler Arası İletişim: REST, gRPC ve Mesajlaşma
Microservice mimarisinde iletişim, tasarım kararlarını hızla etkiler. Servisler arasındaki bağın türü; gecikme, hata yayılımı ve versiyonlama stratejisini belirler. Bu bölümde üç temel yaklaşımı inceleyelim: senkron REST, senkron gRPC ve asenkron mesajlaşma/event.
REST ile Senkron İletişim
REST, HTTP üzerinden anlaşılır ve yaygın bir standart sunduğu için en sık kullanılan yaklaşımdır. Avantajı; basitlik ve tooling zenginliğidir. Dezavantajı ise senkron çağrı zincirlerinin uzamasıyla birlikte hata yayılım riskinin artmasıdır. Bu nedenle REST kullanan sistemlerde zaman aşımı, retry ve circuit breaker gibi desenler kritik hale gelir.
gRPC ile Performans ve Sözleşme
gRPC, Protobuf tabanlı sözleşme odaklı iletişim sunar. Yüksek performans, tip güvenliği ve streaming gibi özellikler; özellikle servis içi (internal) iletişimde güçlü avantaj sağlar. Ancak ekosistem ve debug kolaylığı REST’e göre farklıdır; bu nedenle ekiplerin operasyonel olgunluğu değerlendirilmelidir.
Event-Driven Yaklaşım: Gevşek Bağ ve Ölçek
Asenkron mesajlaşma (Kafka, RabbitMQ gibi) servisler arası gevşek bağlılığı artırır. Özellikle “bir olay oldu, diğer servisler buna tepki versin” senaryolarında etkili bir model sunar. Burada önemli olan; event şemasının yönetimi ve idempotency gibi pratiklerdir. Ayrıca event-driven mimari, veri tutarlılığını çoğu zaman eventual consistency ile ele almayı gerektirir.

Hata Toleransı ve Dayanıklılık: Resilience Patterns
Dağıtık sistemlerde hata “istisna” değil, günlük bir gerçektir. Ağ kopar, servis yavaşlar, bağımlılıklar düşer. Bu yüzden microservice mimarisinde hata toleransı tasarımın içine gömülmelidir. “Hata olunca log basıp tekrar denemek” yaklaşımı, büyük sistemlerde kaskad arızalara yol açabilir.
Timeout, Retry ve Backoff Stratejisi
Timeout olmadan retry yapmak, sistemi kilitlemenin en hızlı yollarından biridir. Her çağrının makul bir zaman aşımı olmalı; retry ise kontrollü ve artan bekleme (exponential backoff) ile uygulanmalıdır. Bazı işlemler için retry uygun değildir; örneğin yan etkili (side-effect) yazma işlemlerinde idempotency sağlanmadıysa tekrarlı istekler veri bozulmasına neden olabilir.
Circuit Breaker ile Kaskad Arızaları Engellemek
Circuit breaker, bir bağımlılık sürekli hata veriyorsa onu geçici olarak devre dışı bırakarak sistemi korur. Bu sayede thread havuzları dolmaz, kuyruklar şişmez ve kullanıcıya daha kontrollü bir geri dönüş verilebilir. Java tarafında Resilience4j gibi kütüphaneler bu desenleri uygulamayı kolaylaştırır.
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import java.time.Duration;
import java.util.function.Supplier;
public class PricingClient {
private final CircuitBreaker cb;
public PricingClient() {
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofSeconds(10))
.slidingWindowSize(20)
.build();
this.cb = CircuitBreaker.of("pricing-service", config);
}
public String getPrice(String sku) {
Supplier<String> decorated = CircuitBreaker
.decorateSupplier(cb, () -> callRemotePricing(sku));
try {
return decorated.get();
} catch (Exception ex) {
return "PRICE_UNAVAILABLE";
}
}
private String callRemotePricing(String sku) {
// HTTP/gRPC call simulation
return "19.99";
}
}Bu örnek, bağımlılık servisinde hata oranı yükselirse devreyi açarak çağrıları keser ve kontrollü bir fallback döndürür. Üretimde bu fallback, kullanıcı deneyimini koruyacak şekilde tasarlanmalı; örneğin en son bilinen değer (cache) veya sınırlı fonksiyon seti gibi.
Bulkhead ve Rate Limiting
Bir servis, farklı türde iş yükleri alıyorsa tek bir thread havuzunu paylaşmak riskli olabilir. Bulkhead yaklaşımı; kaynakları bölerek bir iş tipinin diğerini boğmasını engeller. Rate limiting ise ani trafik artışlarında sistemi korur ve SLO hedeflerini daha stabil tutmanıza yardımcı olur.
Servis Keşfi, Konfigürasyon ve API Gateway
Microservice sayısı arttıkça, “hangi servis nerede çalışıyor?” ve “hangi konfigürasyon hangi ortamda geçerli?” soruları büyür. Statik adresler, manuel ayarlar ve sunucuya özel konfigürasyonlar sürdürülemez hale gelir.
Service Discovery: Dinamik Adresleme
Service discovery, servislerin dinamik olarak bulunmasını sağlar. Kubernetes kullanan ortamlarda bu konu büyük ölçüde platform tarafından çözülür; ancak yine de isimlendirme, health check ve trafik yönlendirme stratejileri tasarım kararlarıdır. Servisin gerçekten sağlıklı olduğundan emin olmak için readiness ve liveness kontrollerinin doğru kurgulanması gerekir.
API Gateway: Dış Dünyaya Tek Kapı
İstemcilerin her servisi ayrı ayrı çağırması hem güvenlik hem de versiyonlama açısından zordur. API gateway, kimlik doğrulama, rate limiting, yönlendirme ve bazı durumlarda response aggregation gibi görevleri üstlenir. Burada dikkat edilmesi gereken; gateway’in “iş mantığına” kaymaması, yani domain logic’in gateway içinde birikmemesidir.
Gözlemlenebilirlik: Log, Metric ve Distributed Tracing
Microservice mimarisinde “hangi istek nerede yavaşladı?” sorusunun cevabı, tek bir log dosyasında bulunmaz. Bu yüzden gözlemlenebilirlik, bir lüks değil gerekliliktir. Doğru metrikler, izleme panelleri ve iz sürme (tracing) olmadan üretim ortamında sorun çözmek maliyetli ve yavaştır.
Correlation ID ve Yapılandırılmış Loglama
Her isteğe bir correlation ID eklemek, servisler arası akışı takip etmeyi kolaylaştırır. Yapılandırılmış log (JSON gibi) kullanmak; arama, filtreleme ve alarm üretmeyi hızlandırır. Ayrıca kişisel verilerin loglanmaması gibi güvenlik ve uyumluluk ihtiyaçları da log tasarımının parçasıdır.
Tracing ile Uçtan Uca Akış
Distributed tracing, bir isteğin servisler arasında nasıl gezdiğini ve nerede beklediğini gösterir. Bu yaklaşım; latency optimizasyonu, kapasite planlama ve regresyon tespiti için güçlüdür. Tracing’i eklemek kadar, örnekleme (sampling) ve veri maliyeti yönetimi de önemlidir.

Veri Tutarlılığı: Saga, Outbox ve Eventual Consistency
Microservice mimarisinde her servisin kendi verisini yönetmesi, klasik ACID işlemlerini servis sınırları içinde bırakır. Birden fazla servis arasında “tek transaction” yapmak çoğu zaman gerçekçi değildir. Bu noktada eventual consistency yaklaşımı devreye girer: Sistem anlık olarak tutarsız görünebilir, ancak süreç tamamlandığında tutarlılığa ulaşır.
Saga Pattern ile İş Süreci Orkestrasyonu
Saga, çok adımlı iş süreçlerini, her adımın kendi lokal transaction’ı olacak şekilde yönetir. Bir adım başarısız olursa, telafi (compensation) adımları çalıştırılır. Saga orkestrasyon (merkezi) veya koreografi (event tabanlı) şeklinde tasarlanabilir. Seçim; ekip yapısı ve akışın karmaşıklığına bağlıdır.
Outbox Pattern ile Güvenilir Event Yayını
Bir servisin veri yazdığı anda event yayınlaması gerekirken, “DB yazıldı ama event gidemediyse?” problemi ortaya çıkar. Outbox pattern; event’i aynı transaction içinde outbox tablosuna yazar, sonra ayrı bir süreç bu kayıtları broker’a gönderir. Böylece “en az bir kez” yayın garantisi daha yönetilebilir hale gelir.
-- Outbox table (simplified)
CREATE TABLE outbox_event (
id VARCHAR(36) PRIMARY KEY,
aggregate_id VARCHAR(36) NOT NULL,
type VARCHAR(100) NOT NULL,
payload TEXT NOT NULL,
created_at TIMESTAMP NOT NULL,
published_at TIMESTAMP NULL
);
-- Transactional write example (pseudo)
-- 1) UPDATE orders SET status='PAID' WHERE id='...';
-- 2) INSERT INTO outbox_event(id, aggregate_id, type, payload, created_at)
-- VALUES ('uuid', 'orderId', 'OrderPaid', '{...}', NOW());Bu yaklaşımın tamamlayıcısı, tüketici tarafında idempotent işlem yapmaktır. Aynı event iki kez gelirse, ikinci kez işlendiğinde sistemin bozulmaması gerekir. Bu pratik, özellikle “en az bir kez” teslim garantisi olan broker’larda hayati öneme sahiptir.
Dağıtım ve Operasyon: CI/CD, Container ve Ortam Stratejisi
Microservice’in faydası, bağımsız deploy edilebilmesidir; ama bu aynı zamanda daha fazla deploy anlamına gelir. Bu nedenle otomasyon, standartlaştırılmış pipeline’lar ve ortam yönetimi olmazsa operasyon yükü hızla artar.
Versiyonlama ve Geriye Dönük Uyumluluk
API değişikliklerinde geriye dönük uyumluluk, entegrasyonların kırılmaması için şarttır. REST tarafında alan eklemek genelde güvenlidir; alan silmek veya anlam değiştirmek risklidir. Event şemalarında ise şema evrimi daha disiplinli yürütülmelidir. Buradaki hedef, servislerin “aynı anda” deploy edilmesini zorunlu kılmayan bir entegrasyon modelidir.
Ortam Ayrımı ve Gizli Bilgiler
Geliştirme, test ve üretim ortamlarında konfigürasyonların yönetimi standart hale getirilmelidir. Gizli bilgiler (anahtarlar, parolalar) kaynak koda girmemeli; secret yönetimiyle taşınmalıdır. Ayrıca servislerin farklı ortamlar arasında taşınabilir olması, platform bağımlılığını azaltır.
Yaygın Hatalar ve İyi Uygulamalar: Pratik Kontrol Listesi
Microservice yolculuğunda başarı, genellikle küçük ama kritik prensiplere bağlıdır. Aşağıdaki listeyi bir “sağlık kontrolü” gibi kullanabilirsiniz:
- Servis sınırlarını iş yeteneklerine göre tanımla, teknik katmanlara göre bölme.
- Paylaşılan veritabanından kaçın; veri sahipliğini netleştir.
- Senkron çağrı zincirlerini kısa tut; asenkron yaklaşımı doğru yerde kullan.
- Timeout, retry ve circuit breaker olmadan üretime çıkma.
- Gözlemlenebilirliği ilk günden kur: metrik, log ve tracing birlikte düşün.
- Sözleşme yönetimini ciddiye al: API ve event şemalarını planlı evrilt.
Bu maddeler, “microservice her şeyi çözer” yanılsamasından uzaklaştırır ve mimariyi yaşayan bir sistem olarak ele almanıza yardımcı olur. Unutmayın: Microservice, ekiplerin çalışma biçimiyle doğrudan ilişkilidir; sadece teknoloji değişimi değildir.
Sonuç ve Öğrenme Yol Haritası
Microservice mimarisi; doğru servis sınırları, dengeli iletişim tercihleri ve güçlü hata toleransı ile birleştiğinde büyük ölçekli sistemlerde önemli avantajlar sunar. Java ekosistemi bu yolculuk için zengin kütüphane ve framework seçenekleri sağlar; ancak esas farkı yaratan, tasarım disiplinleri ve operasyonel olgunluktur.
Eğer Java ile servis tasarımı, servisler arası iletişim seçenekleri, resiliency desenleri ve üretim pratiklerini daha sistematik öğrenmek isterseniz şu sayfaya göz atabilirsiniz: Java Microservices Eğitimi. Buradaki yaklaşım, sadece kod yazmayı değil, aynı zamanda sürdürülebilir bir microservice ekosistemi kurmayı hedefler.
Özetle: microservice nedir sorusunun gerçek cevabı, “servis sayısı” değil; bağımsızlık, dayanıklılık ve gözlemlenebilirlik üçlüsünü birlikte tasarlayabilmektir. Bu üçlüyü baştan planladığınızda, sisteminiz büyürken kontrolü kaybetmezsiniz.


