Microservices

Microservices Mimarisi ve Uygulamaları

Noves TeamNoves Team
27 dk okuma
Microservices Mimarisi ve Uygulamaları

Modern yazılım dünyasında, kullanıcı deneyimi ve ölçeklenebilirlik en kritik faktörler haline geldi. Büyük ölçekli projelerde monolitik yapıların sunduğu sınırlamalar, ekipleri daha esnek ve sürdürülebilir çözümlere yönlendiriyor. İşte tam bu noktada microservices mimarisi devreye giriyor. Her biri bağımsız çalışan, kendi veritabanını ve iş mantığını barındıran küçük servislerden oluşan bu yapı, hem geliştirme hızını artırıyor hem de üretim ortamında daha güvenilir sistemler sunuyor.

Bu makalede, microservices'in temel kavramlarından başlayarak servis tasarımı, izlenebilirlik, dağıtım stratejileri, güvenlik ve gerçek dünya uygulamalarına kadar geniş bir yelpazeyi ele alacağız. Amacımız, teknik detayları anlaşılır bir dille aktararak yazılım ekiplerinin karar alma süreçlerine katkı sağlamak. Noves Digital olarak, bu alanda edindiğimiz deneyimleri de paylaşarak pratik ve uygulanabilir bir rehber sunmayı hedefliyoruz.


Temel Kavramlar ve Avantajlar

Microservices nedir; monolitten farkları

Microservices mimarisi, bir uygulamayı bağımsız olarak geliştirilebilen, dağıtılabilen ve ölçeklenebilen küçük servislere bölme yaklaşımıdır. Her servis belirli bir iş yeteneğine odaklanır ve kendi veritabanı, API'si ile çalışır. Bu yapı, geliştirme ekiplerinin farklı teknoloji yığınları kullanmasına olanak tanır. Örneğin, bir ekip Node.js ile ödeme servisi geliştirirken diğeri Go ile kullanıcı yönetimi servisini yazabilir.

Monolitik mimari ile karşılaştırıldığında, temel farklar nettir. Monolitik uygulamalarda tüm kod tek bir kod tabanında birleşir; bu durum, küçük bir değişikliğin bile tüm sistemi yeniden dağıtmasını gerektirir. Microservices'te ise sadece değişen servis güncellenir. Test edilebilirlik açısından da microservices öne çıkar; her servis izole edilerek bağımsız test edilebilir. Ancak bu esneklik, ağ gecikmesi, veri tutarlılığı ve dağıtık sistem karmaşıklığı gibi zorlukları da beraberinde getirir.

# Monolitik vs Microservices karşılaştırması
monolitik:
  deploy: "Tek bir birim olarak dağıtılır"
  scale: "Tüm uygulama ölçeklenir"
  tech_stack: "Tek bir dil/framework"

microservices:
  deploy: "Her servis bağımsız dağıtılır"
  scale: "Sadece yoğun servis ölçeklenir"
  tech_stack: "Servis başına farklı teknoloji"

Bağımsız dağıtım ve ölçek avantajları nasıl çalışır

Bağımsız dağıtım, microservices mimarisinin en güçlü yönlerinden biridir. Her servis kendi CI/CD boru hattına sahip olabilir; bu sayede bir geliştirici, kullanıcı servisinde yaptığı değişikliği diğer servisleri etkilemeden üretime alabilir. Bu yaklaşım, agile süreçlerle mükemmel uyum sağlar ve ekiplerin haftalık hatta günlük sürüm çıkmasını mümkün kılar. Sektörde bu yeteneğe sahip ekipler, piyasaya çıkış süresini (time-to-market) dramatik şekilde kısaltır.

Ölçekleme açısından ise microservices, kaynakları verimli kullanma imkanı sunar. Monolitik bir uygulamada tüm sistem ölçeklenirken, microservices'te yalnızca yük altındaki servisler yatay olarak genişletilir. Örneğin, e-ticaret platformunda Black Friday döneminde sepet servisi yoğunlaşırken kullanıcı profili servisi aynı seviyede ölçeklenmeyebilir. Bu, bulut maliyetlerini optimize eder ve performans optimizasyonu sağlar. Kubernetes gibi orkestrasyon araçları, autoscaling politikaları ile bu süreci otomatikleştirir.

# Kubernetes HPA (Horizontal Pod Autoscaler) örneği
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: sepet-servisi-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: sepet-servisi
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Servis sınırları ve bounded context örnekleri

Bounded context, Domain-Driven Design (DDD) kavramıdır ve microservices'te servis sınırlarını belirlemek için temel bir rehberdir. Her bounded context, kendi evrensel diline (ubiquitous language), veri modeline ve iş kurallarına sahip bağımsız bir iş alanını temsil eder. Bu sınırlar, servisler arasındaki bağımlılıkları azaltır ve ekiplerin otonom çalışmasını destekler.

Örneğin, bir e-ticaret sisteminde "ürün" kavramı farklı bounded context'lerde farklı anlamlar taşır. Katalog servisinde ürün, fiyat ve açıklama bilgileriyle ilgilidir; stok servisinde ise miktar ve depo konumu önemlidir; sepet servisinde ise seçilen varyasyon ve indirim bilgileri vardır. Her servis kendi "ürün" modelini tanımlar ve API üzerinden iletişim kurar. Bu ayrım, servislerin birbirinin veri modeline bağımlı olmasını engeller.

Katalog Servisi (Product Context):
  - product_id, name, description, price, category

Stok Servisi (Inventory Context):
  - product_id, warehouse_id, quantity, reserved_amount

Sepet Servisi (Cart Context):
  - cart_id, product_id, variant, quantity, discount_applied

Servis Tasarımı ve Modülerlik

API tasarımı nedir; REST vs gRPC karşılaştırması

API tasarımı, microservices mimarisinin bel kemiğidir. İyi tasarlanmış bir API, servisler arası iletişimi standartlaştırır, entegrasyon maliyetlerini düşürür ve geliştirici deneyimini iyileştirir. API tasarlarken versiyonlama, hata yönetimi, rate limiting ve dokümantasyon gibi konulara dikkat etmek gerekir. Açık standartlar kullanmak ve tutarlı naming convention'lar benimsemek, uzun vadede bakım maliyetlerini azaltır.

REST ve gRPC, microservices iletişiminde en yaygın kullanılan iki protokoldür. REST, HTTP/1.1 üzerinden JSON formatında veri taşır; insan tarafından okunabilir olması, geniş ekosistem desteği ve tarayıcı uyumluluğu avantajları sunar. gRPC ise HTTP/2 üzerinden Protocol Buffers (protobuf) kullanarak binary formatında veri iletir; bu da daha düşük gecikme ve daha yüksek verimlilik sağlar. gRPC, servisler arası iletişim için idealdirken REST, mobil uygulama ve web istemcileri ile entegrasyonda tercih edilir.

// gRPC servis tanımı örneği
syntax = "proto3";

service SepetServisi {
  rpc SepeteEkle(SepetIstek) returns (SepetYanit);
  rpc SepetiGetir(KullaniciId) returns (Sepet);
}

message SepetIstek {
  string kullanici_id = 1;
  string urun_id = 2;
  int32 miktar = 3;
}

Veri yönetimi nasıl yapılır; veri sahipliği örnekleri

Microservices mimarisinde veri sahipliği (data ownership), her servisin kendi verisini yönettiği ve başka servislerin doğrudan veritabanına erişemediği prensiptir. Bu yaklaşım, servislerin bağımsızlığını korur ve gevşek bağlılık (loose coupling) sağlar. Ancak veri tutarlılığı konusunda yeni zorluklar ortaya çıkar; bu nedenle eventual consistency gibi kavramlar önem kazanır.

Örneğin, bir sipariş oluşturulduğunda sipariş servisi kendi veritabanına kayıt atar; stok servisine ise bir event göndererek stok düşürme talebinde bulunur. Stok servisi bu event'i işleyip kendi veritabanını günceller. Bu süreçte iki veritabanı arasında anlık tutarlılık olmayabilir, ancak sistem kısa sürede kendi kendini düzeltir (eventual consistency). Bu model, CAP teoremi çerçevesinde availability ve partition tolerance'ı önceliklendirir.

# Sipariş servisinde event yayınlama örneği
class SiparisServisi:
    def siparis_olustur(self, siparis: Siparis):
        # Kendi veritabanına kaydet
        self.db.siparis_ekle(siparis)
        
        # Event bus'a sipariş oluşturuldu event'i gönder
        self.event_bus.publish("siparis.olusturuldu", {
            "siparis_id": siparis.id,
            "urun_id": siparis.urun_id,
            "miktar": siparis.miktar
        })

Domain-driven design ile servis bölme nasıl uygulanır

Domain-Driven Design (DDD), karmaşık iş alanlarını anlaşılır alt alanlara (subdomain) ayırarak microservices sınırlarını belirlemek için güçlü bir metodolojidir. İlk adım, iş alanını core domain, supporting subdomain ve generic subdomain olarak sınıflandırmaktır. Core domain, işin rekabet avantajını sağlayan ve en karmaşık kısımdır; burada en yetkin ekip çalışmalıdır.

Servis bölme sürecinde, aggregate kavramı kritik öneme sahiptir. Bir aggregate, tutarlılık sınırıdır ve dışarıdan sadece aggregate root üzerinden erişilir. Örneğin, bir e-ticaret sisteminde "Sipariş" bir aggregate root olabilir ve sipariş kalemleri (line items) bu aggregate içinde yer alır. Her aggregate, potansiyel bir microservices adayıdır. Ancak her aggregate'i ayrı servis yapmak yerine, iş birliği yüksek olanları bir arada tutmak daha verimli olabilir.

E-ticaret Domain Bölme:
├── Core Domain: Sipariş Yönetimi
│   └── Aggregates: Sipariş, Ödeme, İade
├── Supporting: Kullanıcı Yönetimi
│   └── Aggregates: Kullanici, Profil, Adres
└── Generic: Bildirim Servisi
    └── Aggregates: Email, SMS, Push

Görselleştirme ve İzlenebilirlik

Dağıtık izleme (tracing) nedir; OpenTelemetry örnekleri

Dağıtık izleme (distributed tracing), bir isteğin microservices ortamında geçtiği tüm servisleri ve işlemleri zincirleme olarak görüntüleme tekniğidir. Monolitik uygulamalarda tek bir log dosyası yeterliyken, dağıtık sistemlerde bir isteğin hangi servisten hangi servise geçtiğini, her adımda ne kadar zaman harcadığını ve hatanın nerede oluştuğunu anlamak için tracing şarttır. Bu, performans optimizasyonu ve hata ayıklama için vazgeçilmezdir.

OpenTelemetry, CNCF tarafından desteklenen ve tracing, metrik ve log verilerini standart bir formatta toplayan açık kaynak projesidir. Uygulamalara OpenTelemetry SDK entegre edilerek, istekler otomatik olarak trace'lere dönüştürülür. Her trace, span'lerden oluşur ve span'ler arasında parent-child ilişkisi kurulur. Bu sayede bir API çağrısının veritabanı sorgusu, cache erişimi ve harici API çağrısı gibi alt adımları ayrı ayrı incelenebilir.

# OpenTelemetry ile span oluşturma örneği
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider

tracer = trace.get_tracer(__name__)

with tracer.start_as_current_span("sepet-islemi") as span:
    span.set_attribute("kullanici.id", user_id)
    
    with tracer.start_as_current_span("stok-kontrol"):
        stok_durumu = stok_servisi.kontrol(urun_id)
    
    with tracer.start_as_current_span("fiyat-hesapla"):
        toplam = fiyat_servisi.hesapla(urunler)

Loglama ve merkezi log yönetimi nasıl kurulur

Microservices ortamında her servis kendi loglarını üretir ve bu logların merkezi bir noktada toplanması, analiz edilmesi ve görselleştirilmesi kritik öneme sahiptir. Merkezi log yönetimi, dağıtık sistemlerde hata ayıklama, güvenlik denetimi ve operasyonel izleme için temel altyapıdır. Log formatının standart olması (örneğin JSON), parsing ve sorgulama süreçlerini kolaylaştırır.

Pratikte, ELK Stack (Elasticsearch, Logstash, Kibana) veya Grafana Loki gibi çözümler tercih edilir. Servisler structured logging kullanarak logları stdout'a yazar; bir log agent (Fluentd, Filebeat) bu logları toplayıp merkezi sisteme iletir. Loglarda correlation ID (trace ID) bulundurmak, bir isteğin tüm servislerdeki loglarını birleştirmeyi sağlar. Ayrıca log seviyeleri (INFO, WARN, ERROR) tutarlı kullanılmalı ve PII (kişisel bilgi) loglanmamalıdır.

{
  "timestamp": "2024-01-15T10:30:00Z",
  "level": "ERROR",
  "service": "odeme-servisi",
  "trace_id": "abc123def456",
  "message": "Ödeme işlemi başarısız",
  "context": {
    "kullanici_id": "usr_789",
    "hata_kodu": "KART_GECERSIZ"
  }
}

Servis haritaları ve bağımlılık görselleştirme örnekleri

Servis haritaları, microservices ekosistemindeki servislerin birbirleriyle olan ilişkilerini, iletişim yönlerini ve bağımlılıklarını görsel olarak sunar. Bu haritalar, yeni geliştiricilerin sistemi anlamasını hızlandırır, teknik borçların tespitini kolaylaştırır ve planlanan değişikliklerin etki analizini destekler. Özellikle onlarca servisin olduğu büyük sistemlerde, bu görselleştirme hayati öneme sahiptir.

Araçlar tarafında Kiali (Istio için), Weave Scope veya özel dashboard'lar kullanılabilir. Bu araçlar, gerçek zamanlı trafik verilerini analiz ederek servisler arasındaki çağrı frekansını, hata oranlarını ve gecikme sürelerini gösterir. Örneğin, bir servis haritasında "sepet servisi"nin "stok servisi"ne yaptığı çağrıların %5'inin başarısız olduğu görülebilir; bu, circuit breaker devreye girmesi veya stok servisinin ölçeklendirilmesi gerektiğine işaret eder.

graph LR
    A[Web Uygulaması] --> B[API Gateway]
    B --> C[Kullanıcı Servisi]
    B --> D[Sepet Servisi]
    B --> E[Sipariş Servisi]
    D --> F[Stok Servisi]
    E --> F
    E --> G[Ödeme Servisi]
    G --> H[Banka API]

Yerleşim ve Dağıtım Stratejileri

Container tabanlı dağıtım nedir; Docker kullanım örnekleri

Container teknolojisi, uygulamaları ve bağımlılıklarını izole edilmiş, taşınabilir ve tutarlı ortamlarda çalıştırma imkanı sunar. Docker, bu alanda en yaygın kullanılan platformdur ve microservices'in dağıtımını kolaylaştıran temel bir yapı taşıdır. Her microservice, kendi Dockerfile'ı ile container imajına dönüştürülür; bu imaj, geliştirme ortamından üretim ortamına değişmeden taşınabilir. "Bende çalışıyordu" sorununu ortadan kaldırır.

Dockerfile yazarken çok aşamalı build (multi-stage build) kullanmak, imaj boyutlarını küçültür ve güvenlik yüzeyini daraltır. Örneğin, bir Go uygulaması için build aşamasında tam geliştirme ortamı kullanılırken, son aşamada sadece derlenmiş binary'nin çalıştırıldığı minimal bir imaj (distroless veya alpine) oluşturulur. Ayrıca, container'lar arası iletişim için Docker network'leri veya docker-compose kullanılabilir; bu, yerel geliştirme ortamında tüm servislerin birlikte çalışmasını sağlar.

# Multi-stage Dockerfile örneği
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o sepet-servisi

FROM gcr.io/distroless/static:nonroot
COPY --from=builder /app/sepet-servisi /sepet-servisi
EXPOSE 8080
USER nonroot:nonroot
ENTRYPOINT ["/sepet-servisi"]

Kubernetes ile orkestrasyon nasıl yapılır; temel kavramlar

Kubernetes, container'ların otomatik dağıtımı, ölçeklenmesi ve yönetimi için kullanılan açık kaynaklı bir orkestrasyon platformudur. Microservices mimarisinde onlarca hatta yüzlerce container'ı manuel yönetmek imkansızdır; Kubernetes bu noktada hayati bir rol üstlenir. Pod, Deployment, Service, Ingress, ConfigMap ve Secret gibi temel kavramlar, uygulamaların declarative olarak tanımlanmasını ve istenen durumun sürekli korunmasını sağlar.

Bir Pod, Kubernetes'in en küçük dağıtım birimidir ve bir veya daha fazla container'ı gruplar. Deployment, pod'ların istenen replica sayısını korur ve rolling update/strategy ile kesintisiz dağıtım sağlar. Service, pod'lara sabit bir IP ve DNS adı atayarak servis keşfini (service discovery) kolaylaştırır. Ingress ise HTTP/HTTPS trafiğini yönlendirir. Kubernetes, ayrıca health check (liveness ve readiness probe), resource limit ve node affinity gibi gelişmiş özelliklerle üretim ortamında kararlılık sağlar.

# Kubernetes Deployment ve Service örneği
apiVersion: apps/v1
kind: Deployment
metadata:
  name: siparis-servisi
spec:
  replicas: 3
  selector:
    matchLabels:
      app: siparis-servisi
  template:
    metadata:
      labels:
        app: siparis-servisi
    spec:
      containers:
      - name: siparis
        image: registry/siparis-servisi:v1.2.0
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10

Bulut vs kenar (edge) yerleşimi karar kriterleri

Microservices'in nerede çalıştırılacağı, iş gereksinimlerine, maliyet yapısına ve performans beklentilerine göre değişir. Bulut yerleşimi (cloud deployment), esnek ölçekleme, yüksek kullanılabilirlik ve operasyonel yükün servis sağlayıcıya devredilmesi avantajları sunar. AWS, Azure veya GCP gibi platformlar, managed Kubernetes, serverless fonksiyonlar ve managed veritabanları ile ekiplerin altyapı yönetimi yerine iş mantığına odaklanmasını sağlar. SaaS ürünleri için bulut, varsayılan tercihtir.

Kenar (edge) yerleşimi ise verinin üretildiği yere yakın konumlarda işlem yapma prensibine dayanır. IoT cihazları, oyun sunucuları veya cross-platform mobil uygulamalar için düşük gecikme kritik öneme sahipse edge computing tercih edilir. Örneğin, bir akıllı kamera görüntüyü buluta göndermek yerine yerel olarak yapay zeka modeli ile analiz edebilir. Karar verirken gecikme gereksinimleri, veri yerel düzenlemeleri, bant genişliği maliyetleri ve operasyonel karmaşıklık göz önünde bulundurulmalıdır. Hibrit yaklaşımlar, her iki dünyanın avantajlarını birleştirir.

Çok bölgeli dağıtım ve veri tutarlılığı stratejileri

Çok bölgeli dağıtım, uygulamaların birden fazla coğrafi bölgede çalıştırılmasıdır. Bu yaklaşım, felaket kurtarma (disaster recovery), yasal veri yerleşim gereklilikleri ve kullanıcılara yakın sunucu ile düşük gecikme sağlar. Ancak veri tutarlılığı konusunda önemli zorluklar getirir. Bir bölgedeki veritabanı yazma işlemi, diğer bölgedeki replikaya ne kadar sürede yansıyacaktır?

Bu soruna yönelik stratejiler şunlardır: Active-Passive, bir bölge aktifken diğeri yedek durumdadır; failover durumunda aktif olur. Active-Active, her iki bölge de eşzamanlı olarak yazma kabul eder; bu durumda çakışma çözümleme (conflict resolution) mekanizmaları gerekir. Eventual consistency, kısa süreli tutarsızlıkları kabul ederek yüksek kullanılabilirlik sağlar. Strong consistency ise tüm replikaların senkronize olmasını bekler ancak gecikmeyi artırır. CRDT (Conflict-free Replicated Data Types) gibi yapılar, active-active senaryolarda tutarlılığı matematiksel olarak garanti eder.

Çok Bölgeli Senaryo:
┌─────────────┐         ┌─────────────┐
│  eu-west-1  │ ◄─────► │ us-east-1   │
│  (Aktif)    │  Replikasyon │  (Aktif)    │
└─────────────┘         └─────────────┘
       │                       │
   Yerel kullanıcılar      Yerel kullanıcılar

Gelişmiş Mimariler ve Desenler

Event-driven mimari nedir; mesajlaşma örnekleri

Event-driven mimari (EDA), servislerin doğrudan birbirlerini çağırmak yerine event'ler aracılığıyla iletişim kurduğu bir yaklaşımdır. Bir serviste meydana gelen durum değişikliği (event), mesaj kuyruğuna veya event bus'a yayınlanır; ilgilenen servisler bu event'i dinleyerek kendi iş mantığını çalıştırır. Bu model, servisler arası gevşek bağlılığı maksimize eder ve sistemin genişlemesine olanak tanır.

Mesajlaşma araçları olarak Apache Kafka, RabbitMQ, NATS ve AWS SNS/SQS gibi çözümler yaygın olarak kullanılır. Kafka, yüksek throughput ve log-based persistence ile büyük ölçekli event streaming için idealdir. RabbitMQ, routing ve queueing yetenekleriyle esnek mesajlaşma sunar. Event-driven mimaride event şemalarının yönetimi (Schema Registry) ve event versioning kritik öneme sahiptir; aksi halde tüketicilerin event'i yanlış yorumlaması riski vardır.

# Kafka producer örneği
from kafka import KafkaProducer
import json

producer = KafkaProducer(
    bootstrap_servers=['kafka:9092'],
    value_serializer=lambda v: json.dumps(v).encode('utf-8')
)

event = {
    "event_type": "siparis.olusturuldu",
    "siparis_id": "sip_12345",
    "kullanici_id": "usr_789",
    "tarih": "2024-01-15T10:30:00Z"
}

producer.send('siparis-events', event)
producer.flush()

Saga ve dağıtık işlem desenleri nasıl uygulanır

Microservices'te birden fazla servisi kapsayan işlemler (distributed transactions), ACID garantilerini zorlaştırır. Saga deseni, bu sorunu her servisin kendi yerel transaction'ını yürütmesi ve başarısızlık durumunda compensating transaction'lar (telafi işlemleri) çalıştırması prensibiyle çözer. İki temel saga implementasyonu vardır: Choreography ve Orchestration.

Choreography-based saga'da her servis bir event'i dinler, kendi işlemini yapar ve bir sonraki event'i yayınlar. Merkezi bir koordinatör yoktur; bu basitlik sağlar ancak karmaşık akışların takibini zorlaştırır. Orchestration-based saga'da ise bir Saga Orchestrator, tüm adımları sırayla yönetir ve hata durumunda compensating'leri tetikler. Örneğin, bir seyahat rezervasyon sisteminde: uçak bileti rezerve edilir, otel rezerve edilir, araç kiralanır. Herhangi bir adım başarısız olursa önceki adımların rezervasyonları iptal edilir.

# Saga orchestrator örneği (basit Python implementasyonu)
class RezervasyonSaga:
    def execute(self, talep):
        try:
            ucak_id = ucak_servisi.rezervasyon_yap(talep)
            otel_id = otel_servisi.rezervasyon_yap(talep)
            arac_id = arac_servisi.kiralama_yap(talep)
            return {"durum": "basarili", "ucak": ucak_id, "otel": otel_id, "arac": arac_id}
        except UcakRezervasyonHatasi:
            return {"durum": "basarisiz", "adim": "ucak"}
        except OtelRezervasyonHatasi:
            ucak_servisi.iptal_et(ucak_id)
            return {"durum": "basarisiz", "adim": "otel"}
        except AracKiralamaHatasi:
            ucak_servisi.iptal_et(ucak_id)
            otel_servisi.iptal_et(otel_id)
            return {"durum": "basarisiz", "adim": "arac"}

Circuit breaker, retry ve backoff uygulama örnekleri

Dağıtık sistemlerde servisler arası çağrılar başarısız olabilir; ağ sorunları, servis çökmeleri veya aşırı yüklenme gibi nedenlerle. Circuit breaker deseni, arızalı bir servise sürekli çağrı yapılmasını engelleyerek kaskade hataları (cascading failures) önler. Üç durumu vardır: Closed (normal çalışma), Open (çağrıları reddetme) ve Half-Open (sınırlı test çağrıları). Hata oranı bir eşiği aştığında circuit açılır, belirli bir süre sonra half-open durumuna geçer ve servis sağlıklıysa tekrar closed olur.

Retry ve exponential backoff ise geçici hataların (transient failures) üstesinden gelmek için kullanılır. Bir çağrı başarısız olduğunda hemen tekrar denemek yerine, her deneme arasında artan sürelerle (örneğin 1sn, 2sn, 4sn) beklenir. Bu, aşırı yüklenmiş bir servisin daha da baskı altına girmesini engeller. Jitter (rastgele gecikme ekleme) ile backoff sürelerine çeşitlilik katılarak "thundering herd" sorunu önlenir.

# Circuit breaker ve retry örneği (Resilience4j benzeri)
import time
import random

class CircuitBreaker:
    def __init__(self, hata_esigi=5, bekleme_suresi=30):
        self.hata_sayisi = 0
        self.durum = "CLOSED"  # CLOSED, OPEN, HALF_OPEN
        self.hata_esigi = hata_esigi
        self.bekleme_suresi = bekleme_suresi
        self.son_hata_zamani = None
    
    def cagri_yap(self, fonksiyon, *args):
        if self.durum == "OPEN":
            if time.time() - self.son_hata_zamani > self.bekleme_suresi:
                self.durum = "HALF_OPEN"
            else:
                raise Exception("Circuit breaker AÇIK - çağrı reddedildi")
        
        try:
            sonuc = fonksiyon(*args)
            self.hata_sayisi = 0
            self.durum = "CLOSED"
            return sonuc
        except Exception as e:
            self.hata_sayisi += 1
            self.son_hata_zamani = time.time()
            if self.hata_sayisi >= self.hata_esigi:
                self.durum = "OPEN"
            raise e

Performans, Ölçeklenebilirlik ve Optimizasyon

Yatay ölçekleme ve autoscaling nasıl çalışır

Yatay ölçekleme (horizontal scaling), bir uygulamayı daha güçlü bir sunucuya taşımak (vertical scaling) yerine, daha fazla sunucu/eklenti ekleyerek yükü dağıtma stratejisidir. Microservices mimarisi, yatay ölçeklemeyi doğal olarak destekler çünkü her servis bağımsız olarak çoğaltılabilir. Bu yaklaşım, maliyet etkinliği ve hata toleransı açısından üstündür; tek bir sunucunun arızalanması tüm sistemi durdurmaz.

Autoscaling, yatay ölçeklemenin otomatik versiyonudur. Kubernetes'te HPA (Horizontal Pod Autoscaler) CPU/memory kullanımına göre pod sayısını ayarlar. Daha gelişsen senaryolarda custom metrics (örneğin kuyruk uzunluğu, istek sayısı) kullanılabilir. Cluster Autoscaler ise node seviyesinde ölçekleme yaparak kaynak ihtiyacına göre yeni sunucular ekler veya kaldırır. Autoscaling politikaları tasarlarken, ölçekleme gecikmesi (scale-up süresi), maliyet dalgalanmaları ve minimum replica sayısı gibi faktörler dikkate alınmalıdır.

# Custom metrics ile autoscaling
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: siparis-servisi-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: siparis-servisi
  minReplicas: 3
  maxReplicas: 50
  metrics:
  - type: Pods
    pods:
      metric:
        name: kafka_consumer_lag
      target:
        type: AverageValue
        averageValue: "100"
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Pods
        value: 5
        periodSeconds: 60

Cache stratejileri ve veri tutarlılığı örnekleri

Cache, microservices performansını dramatik şekilde artıran bir tekniktir; ancak dağıtık ortamda cache invalidation (geçersiz kılma) zorluğu vardır. Cache-aside (lazy loading), uygulamanın cache'e bakıp cache'te yoksa veritabanından çekip cache'e yazdığı en yaygın stratejidir. Write-through ise veri yazılırken hem veritabanına hem cache'e yazılır. Write-behind ise veriyi önce cache'e yazar ve arka planda veritabanına senkronize eder.

Dağıtık sistemlerde cache tutarlılığı için cache invalidation event'leri kullanılabilir. Bir servis veriyi güncellediğinde, bir event yayınlar ve diğer servislerin cache'lerini temizler. Redis, Redis Cluster veya Redis Sentinel ile yüksek kullanılabilirlik sağlar. Cache stampede (aynı anda çok sayıda cache miss) önlemek için cache warming veya probabilistic early expiration kullanılabilir. TTL (Time To Live) değerlerinin dikkatli seçilmesi, eski verinin ne kadar süreyle sunulacağını belirler.

# Cache-aside örneği
import redis

r = redis.Redis(host='redis', port=6379, decode_responses=True)

def kullanici_getir(kullanici_id):
    cache_anahtari = f"kullanici:{kullanici_id}"
    
    # 1. Cache'e bak
    cached = r.get(cache_anahtari)
    if cached:
        return json.loads(cached)
    
    # 2. Cache'te yoksa veritabanından çek
    kullanici = db.query("SELECT * FROM kullanicilar WHERE id = %s", kullanici_id)
    
    # 3. Cache'e yaz (TTL: 1 saat)
    r.setex(cache_anahtari, 3600, json.dumps(kullanici))
    
    return kullanici

Gecikme azaltma ve kaynak optimizasyonu teknikleri

Microservices ortamında gecikme (latency), kullanıcı deneyimini doğrudan etkiler. Gecikme azaltma teknikleri arasında asenkron işleme, connection pooling, compression ve CDN kullanımı öne çıkar. Servisler arası her çağrı, ağ gecikmesi (network latency) ekler; bu nedenle gereksiz çağrıları elimine etmek veya birden fazla çağrıyı tek bir istekte toplamak (batching) önemlidir. gRPC gibi binary protokoller, JSON'a göre daha hızlı serileştirme sunar.

Kaynak optimizasyonu ise altyapı maliyetlerini düşürürken performansı koruma sanatıdır. Container'lar için uygun resource request/limit değerleri belirlemek, over-provisioning'i önler. Vertical Pod Autoscaler (VPA), pod'ların gerçek kaynak kullanımını analiz ederek öneriler sunar. Node affinity ve pod anti-affinity kuralları, workload'ları verimli şekilde dağıtır. Ayrıca, spot instances ve preemptible VM'ler gibi maliyet optimize edici bulut özellikleri, fault-tolerant servisler için büyük tasarruf sağlar.

# Resource optimizasyonu örneği
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: api-servisi
    image: registry/api:v1.0
    resources:
      requests:
        memory: "256Mi"
        cpu: "250m"
      limits:
        memory: "512Mi"
        cpu: "500m"
    env:
    - name: GRPC_KEEPALIVE_TIME
      value: "10s"
    - name: CONNECTION_POOL_SIZE
      value: "20"

Uyumluluk, Güvenlik ve Operasyon

Servis kimlik doğrulama ve yetkilendirme nasıl yapılır

Microservices ortamında her servis, gelen isteklerin kimden geldiğini ve ne yapma yetkisi olduğunu doğrulamalıdır. Kimlik doğrulama (authentication), kullanıcının veya servisin kimliğini teyit ederken; yetkilendirme (authorization), bu kimliğin hangi kaynaklara erişebileceğini belirler. Merkezi bir kimlik sağlayıcı (Identity Provider) kullanmak, her serviste ayrı login mekanizması yazma ihtiyacını ortadan kaldırır.

OAuth 2.0 / OpenID Connect, kullanıcı kimlik doğrulaması için endüstri standardıdır. JWT (JSON Web Token) token'ları, kullanıcı bilgilerini ve yetkilerini taşır; servisler token'ı doğrulayarak karar verir. mTLS (mutual TLS) ise servisler arası iletişimde karşılıklı sertifika doğrulaması yaparak kimlik doğrulama sağlar. Istio gibi service mesh çözümleri, mTLS'i otomatik olarak uygular ve geliştiricilerin güvenlik kodu yazmasına gerek kalmaz. RBAC (Role-Based Access Control) ve ABAC (Attribute-Based Access Control) modelleri, esnek yetkilendirme politikaları sunar.

# Istio AuthorizationPolicy örneği
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: sepet-servisi-policy
  namespace: default
spec:
  selector:
    matchLabels:
      app: sepet-servisi
  action: ALLOW
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/default/sa/web-uygulamasi"]
    to:
    - operation:
        methods: ["GET", "POST"]
        paths: ["/api/sepet/*"]

Veri gizliliği ve uyumluluk (GDPR benzeri) uygulamaları

Veri gizliliği düzenlemeleri (GDPR, KVKK, CCPA), microservices mimarisinde özel tasarım kararları gerektirir. Veri minimizasyonu prensibi, sadece gerekli verilerin toplanmasını ve saklanmasını öngörür. Privacy by Design, veri gizliliğinin mimari tasarımın bir parçası olarak ele alınmasını gerektirir. Her servis, hangi kişisel veriyi işlediğini, neden işlediğini ve ne kadar süreyle sakladığını dokümante etmelidir.

Veri anonimleştirme ve pseudonimleştirme teknikleri, analiz amaçlı veri kullanımında kimlik korunmasını sağlar. Right to erasure (unutulma hakkı), bir kullanıcının tüm verilerinin silinmesini talep etme hakkıdır; bu, verinin birden fazla serviste ve veritabanında tutulduğu durumlarda karmaşık bir silme işlemi gerektirir. Data Protection Impact Assessment (DPIA) ve veri işleme kayıtları, düzenleyici denetimlerde kanıt niteliği taşır. Veri aktarım logları ve erişim denetimleri, güvenlik olaylarının incelenmesini sağlar.

# Veri anonimleştirme örneği
import hashlib
from datetime import datetime

def anonimlestir(kullanici_id, tuz="gizli_tuz"):
    """Kullanıcı ID'sini geri döndürülemez şekilde hash'ler"""
    return hashlib.sha256(f"{kullanici_id}{tuz}".encode()).hexdigest()[:16]

def veri_silme_talebi(kullanici_id):
    """Tüm servislerde kullanıcı verisini silme"""
    event = {
        "turu": "kullanici.silme_talebi",
        "kullanici_id": kullanici_id,
        "talep_tarihi": datetime.utcnow().isoformat()
    }
    # Tüm servislere silme event'i gönder
    event_bus.publish("gdpr.silme", event)

Güvenlik duvarı, WAF ve servis izolasyonu örnekleri

Microservices güvenliği, ağ seviyesinden uygulama seviyesine kadar çok katmanlı bir yaklaşım gerektirir. API Gateway, tüm dış trafiğin geçtiği tek giriş noktasıdır ve burada rate limiting, IP whitelist/blacklist ve API key yönetimi uygulanabilir. WAF (Web Application Firewall), SQL injection, XSS ve diğer web tabanlı saldırıları filtreler. Cloudflare, AWS WAF veya mod_security gibi çözümler tercih edilebilir.

Servis izolasyonu, bir servisin güvenlik ihlali durumunda diğerlerine sıçramasını engeller. Kubernetes'te Network Policies, pod'lar arası trafiği kısıtlar; örneğin sadece API Gateway'in sepet servisine erişmesine izin verilir. Pod Security Standards, container'ların ayrıcalıklı modda çalışmasını engeller. Service mesh (Istio, Linkerd), mTLS ile servisler arası trafiği şifreler ve fine-grained access control sağlar. Ayrıca, secret management (HashiCorp Vault, AWS Secrets Manager) ile API anahtarları ve veritabanı şifreleri merkezi olarak yönetilir.

# Kubernetes NetworkPolicy örneği
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: odeme-servisi-izolasyonu
spec:
  podSelector:
    matchLabels:
      app: odeme-servisi
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: siparis-servisi
    ports:
    - protocol: TCP
      port: 8080

Uygulama Senaryoları ve İş Odaklı Kullanım

E-ticaret için mikroservis örnekleri ve sepet yönetimi

E-ticaret, microservices mimarisinin en yaygın ve etkili kullanıldığı alanlardan biridir. Bir e-ticaret platformu; ürün kataloğu, stok yönetimi, sepet, sipariş, ödeme, kargo ve bildirim gibi bağımsız servislerden oluşur. Her servis, kendi iş alanında uzmanlaşmış bir ekibe emanet edilebilir ve farklı teknolojiler kullanabilir. Örneğin, ödeme servisi PCI-DSS uyumluluğu için özel güvenlik gereksinimleri taşırken, katalog servisi yüksek okuma performansı optimizasyonu gerektirir.

Sepet yönetimi, e-ticaret'in kritik ve karmaşık bir parçasıdır. Sepet servisi, kullanıcının seçtiği ürünleri, miktarları ve fiyatları yönetir; ancak stok servisi ile sürekli iletişim halinde olmalıdır. Sepete ürün eklendiğinde stok rezerve edilir, sepet terk edildiğinde rezervasyon iptal edilir. Event-driven mimari burada güçlü bir çözüm sunar: sepet servisi event'ler yayınlar, stok ve fiyat servisleri dinleyerek kendi durumlarını günceller. Bu ayrım, sepet servisinin stok ve fiyat mantığından bağımsız kalmasını sağlar.

# Sepet servisi - sepete ürün ekleme
class SepetServisi:
    def sepete_ekle(self, kullanici_id, urun_id, miktar):
        # Stok kontrolü
        if not self.stok_servisi.musait_mi(urun_id, miktar):
            raise StokYetersizHatasi()
        
        # Sepete ekle
        sepet = self.repo.sepet_getir(kullanici_id)
        sepet.urun_ekle(urun_id, miktar)
        self.repo.kaydet(sepet)
        
        # Event yayınla
        self.event_bus.publish("sepet.urun_eklendi", {
            "kullanici_id": kullanici_id,
            "urun_id": urun_id,
            "miktar": miktar
        })
        
        return sepet

SaaS ürünlerinde çok kiracılı (multi-tenant) mimari nasıl kurulur

Çok kiracılı (multi-tenant) mimari, tek bir uygulama örneğinin birden fazla müşteriye (kiracı) hizmet verdiği bir yaklaşımdır. SaaS ürünleri için bu model, operasyonel verimlilik ve maliyet optimizasyonu sağlar. Microservices ile birleştirildiğinde, kiracı yalıtımı ve kaynak paylaşımı arasında denge kurulması gerekir. Üç temel multi-tenancy modeli vardır: shared database (tüm kiracılar aynı veritabanında, tenant_id ile ayrılır), database per tenant (her kiracıya özel veritabanı) ve schema per tenant (aynı veritabanı, farklı şemalar).

Microservices bağlamında, her servis kendi kiracı yalıtım stratejisini seçebilir. Örneğin, fatura servisi hassas finansal veri içerdiği için database-per-tenant tercih edebilirken, analitik servisi shared-database ile maliyet avantajı sağlayabilir. Tenant context her istekle birlikte taşınmalı ve servisler bu bilgiye göre veritabanı bağlantısı veya şema seçimi yapmalıdır. Resource quota yönetimi, bir kiracının diğerlerini etkilemesini (noisy neighbor problem) önler.

# Multi-tenant veritabanı bağlantısı örneği
class TenantAwareDB:
    def __init__(self, config):
        self.config = config
    
    def get_connection(self, tenant_id):
        # Kiracıya özel veritabanı seçimi
        db_config = self.config.get_tenant_db(tenant_id)
        return create_engine(db_config.connection_string)
    
    def sorgula(self, tenant_id, sql, params):
        with self.get_connection(tenant_id) as conn:
            # SQL injection önlemi + tenant filtreleme
            return conn.execute(text(sql), {**params, "tenant_id": tenant_id})

Web geliştirme ve responsive tasarım ile entegrasyon örnekleri

Microservices mimarisi, modern web geliştirme ve responsive tasarım ile güçlü bir sinerji oluşturur. Frontend, backend servislerinden bağımsız olarak geliştirilebilir ve deploy edilebilir. BFF (Backend for Frontend) deseni, her istemci türüne (web, iOS, Android) özel bir API katmanı sunar; bu sayede mobil uygulama daha az veri alırken web uygulaması daha zengin bir payload alabilir. Bu ayrım, kullanıcı deneyimini optimize eder.

Micro-frontend mimarisi, frontend'i de microservices prensipleriyle parçalara ayırır. Her ekip, kendi frontend modülünü (örneğin sepet bileşeni, ürün detay sayfası) bağımsız geliştirir ve deploy eder. Module Federation (Webpack 5) veya single-spa gibi araçlar, bu parçaların çalışma zamanında birleştirilmesini sağlar. Responsive tasarım, bu mikro-frontend'lerin her biri için ayrı ayrı uygulanabilir; örneğin sepet bileşeni mobilde farklı, masaüstünde farklı davranabilir.

// BFF (Backend for Frontend) örneği
// Web için zengin, mobil için hafif yanıt
app.get('/api/web/sepet/:kullaniciId', async (req, res) => {
    const sepet = await sepetServisi.getir(req.params.kullaniciId);
    const urunDetaylari = await katalogServisi.detaylariGetir(sepet.urunler);
    const oneriler = await oneriServisi.getir(req.params.kullaniciId);
    
    res.json({
        sepet: sepet,
        urunler: urunDetaylari,      // Web için tam detay
        oneriler: oneriler,           // Web için öneriler
        toplam: sepet.toplamHesapla()
    });
});

app.get('/api/mobil/sepet/:kullaniciId', async (req, res) => {
    const sepet = await sepetServisi.getir(req.params.kullaniciId);
    
    res.json({
        urun_sayisi: sepet.urunler.length,
        toplam: sepet.toplamHesapla()
        // Mobil için sadece gerekli alanlar
    });
});

Araçlar, Kütüphaneler ve Mühendislik Pratikleri

CI/CD boru hatları nasıl kurulur; otomasyon örnekleri

CI/CD (Continuous Integration / Continuous Deployment), microservices geliştirme sürecinin omurgasıdır. Her servis için bağımsız bir boru hattı kurulmalı ve bu hat, kod değişikliğinden üretim dağıtımına kadar tüm adımları otomatize etmelidir. CI aşamasında; kod linting, birim testleri, entegrasyon testleri, güvenlik taraması (SAST/DAST) ve imaj build işlemleri yapılır. CD aşamasında ise; imaj kayıt defterine push, staging ortamına dağıtım, smoke testleri ve üretim dağıtımı gerçekleştirilir.

GitHub Actions, GitLab CI, Jenkins veya Azure DevOps gibi araçlar tercih edilebilir. Her servis için Dockerfile ve Helm chart (veya Kustomize) versiyon kontrolünde tutulur. GitOps yaklaşımında, Git reposu tek doğruluk kaynağıdır; ArgoCD veya Flux, Git'teki istenen durumu Kubernetes cluster'ına uygular. Canary deployment ve blue-green deployment stratejileri, riski minimize ederek üretime yeni sürüm çıkmayı sağlar. Feature flag'ler, kodu deploy edip özelliği isteğe bağlı açma imkanı sunar.

# GitHub Actions CI/CD örneği
name: Sepet Servisi CI/CD

on:
  push:
    paths:
      - 'sepet-servisi/**'

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Testleri çalıştır
        run: |
          cd sepet-servisi
          go test ./... -coverprofile=coverage.out
      
  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Docker imajı build et
        run: |
          docker build -t registry/sepet-servisi:${{ github.sha }} .
          docker push registry/sepet-servisi:${{ github.sha }}
  
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Kubernetes'e deploy et
        run: |
          kubectl set image deployment/sepet-servisi \
            sepet=registry/sepet-servisi:${{ github.sha }}

Popüler araçlar: Kubernetes, Istio, Kafka karşılaştırması

Microservices ekosisteminde doğru araç seçimi, projenin başarısını doğrudan etkiler. Kubernetes, container orkestrasyonunun de facto standardıdır; otomatik ölçekleme, self-healing ve zengin ekosistemi ile hemen her projede kullanılır. Istio, Kubernetes üzerinde çalışan bir service mesh'tir; trafik yönetimi, güvenlik (mTLS), observability ve policy enforcement gibi yetenekler sunar. Ancak operational karmaşıklığı ve kaynak tüketimi nedeniyle küçük projelerde ağır gelebilir.

Apache Kafka, yüksek throughput event streaming platformudur; log-based persistence sayesinde event'leri uzun süre saklayabilir ve geriye dönük işleme (replay) imkanı sunar. RabbitMQ, daha geleneksel bir mesaj kuyruğudur; routing, priority queue ve dead letter exchange gibi özellikleriyle esneklik sağlar. Seçim, throughput gereksinimlerine, mesaj kalıcılık beklentilerine ve ekibin uzmanlığına göre yapılmalıdır. Profesyonel ekiplerde, bu araçların birlikte kullanıldığı hibrit senaryolar da yaygındır.

MLOps, observability ve Müşteri odaklı UI/UX entegrasyonu

Microservices mimarisi, MLOps (Machine Learning Operations) için ideal bir zemin sunar. Model eğitimi, inference servisi, feature store ve model registry gibi bileşenler ayrı servisler olarak tasarlanabilir. Feature store, farklı ML modelleri için ortak özellikleri merkezi olarak yönetir. Model servisi, REST veya gRPC API üzerinden tahmin sunar ve A/B test için farklı model versiyonlarına yönlendirme yapılabilir. Kubernetes, GPU workload'larını yöneterek ML inference'ı ölçekler.

Observability, yalnızca teknik bir gereksinim değil, aynı zamanda müşteri odaklı ürün geliştirme için stratejik bir araçtır. Kullanıcı davranışlarını izlemek, hangi özelliklerin kullanıldığını, hangi akışların terk edildiğini ve performansın kullanıcı deneyimini nasıl etkilediğini gösterir. Bu veriler, product manager ve UX designer'ların kararlarını bilgilendirir. Real User Monitoring (RUM) araçları, frontend performans metriklerini (Core Web Vitals) toplar ve kullanıcı deneyimi ile teknik metrikler arasında köprü kurar.

# ML inference servisi Kubernetes deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: oneri-model-servisi
spec:
  replicas: 2
  selector:
    matchLabels:
      app: oneri-model
  template:
    spec:
      containers:
      - name: model
        image: registry/oneri-model:v2.1
        resources:
          limits:
            nvidia.com/gpu: 1
        ports:
        - containerPort: 8080
        env:
        - name: MODEL_PATH
          value: "/models/oneri-v2.onnx"

Sonuçlar, Ölçümleme ve Geçiş Stratejileri

Başarı metrikleri nedir; SLA, SLO ve iş KPI örnekleri

Microservices projelerinin başarısı, yalnızca teknik metriklerle değil, iş sonuçlarıyla da ölçülmelidir. SLA (Service Level Agreement), müşteriye vaat edilen hizmet seviyesidir; örneğin "API yanıt süresi %99.9 oranında 200ms altında olacaktır." SLO (Service Level Objective), iç hedeflerdir ve genellikle SLA'dan daha sıkıdır. SLI (Service Level Indicator) ise ölçülen gerçek değerdir; latency, hata oranı, throughput gibi metriklerdir.

İş KPI'ları ise teknik metrikleri iş değerine dönüştürür: deployment frequency (ne sıklıkla üretime çıkılıyor), lead time for changes (koddan üretime geçiş süresi), mean time to recovery (MTTR) (arıza sonrası toparlanma süresi) ve change failure rate (deploy sonrası hata oranı) DORA metrikleri olarak bilinir. Bu metrikler, ekip verimliliğini ve sistem güvenilirliğini ölçer. Ayrıca, kullanıcı deneyimi metrikleri (conversion rate, sepet terk oranı, sayfa yükleme süresi) de microservices başarısının nihai göstergeleridir.

Örnek SLO Tanımları:
┌─────────────────────┬─────────────────┬─────────────────┐
│ Metrik              │ Hedef (SLO)     │ Uyarı (Alert)   │
├─────────────────────┼─────────────────┼─────────────────┤
│ API Latency (p99)   │ < 300ms         │ > 500ms         │
│ Hata Oranı          │ < 0.1%          │ > 0.5%          │
│ Kullanılabilirlik   │ > 99.95%        │ < 99.9%         │
│ Deploy Frequency    │ > 10/gün        │ < 5/gün         │
└─────────────────────┴─────────────────┴─────────────────┘

Monolitten microservices'e geçiş adımları ve risk yönetimi

Monolitik bir uygulamadan microservices'e geçiş, büyük bir dönüşüm projesidir ve dikkatli planlama gerektirir. Strangler Fig deseni, geçişin en güvenli yoludur; monolitik uygulamaya yeni özellikler mikroservis olarak yazılır ve monolit yavaş yavaş "sarar." Bu yaklaşım, büyük patlama (big bang) riskini ortadan kaldırır. İlk adım, monolitik kod tabanını modüllere ayırmak ve modüller arasındaki bağımlılıkları azaltmaktır.

Risk yönetimi açısından, geçiş sırasında veri tutarlılığı en büyük zorluktur. Veritabanı bölme (database per service) aşamasında, CQRS ve event sourcing gibi desenler geçişi kolaylaştırabilir. Ayrıca, feature toggles kullanarak yeni servislerin isteğe bağlı devreye alınması sağlanır. Ekip yapısının da dönüşmesi gerekir; Conway's Law'a göre sistem mimarisi, iletişim yapısını yansıtır. Bu nedenle, cross-functional ekipler oluşturmak ve her ekibe bir servis alanı vermek önemlidir.

Strangler Fig Geçiş Planı:
Faz 1: Monolitik uygulamayı modüllere ayır (3-6 ay)
Faz 2: En az bağlı modülü mikroservise dönüştür (2-3 ay)
Faz 3: API Gateway kur, trafiği yönlendir (1-2 ay)
Faz 4: Kalan modülleri sırayla dönüştür (6-12 ay)
Faz 5: Monolitik kodu tamamen emekliye ayır (2-3 ay)

Proje yol haritası: prototip, pilot, üretim geçişi nasıl planlanır

Microservices geçişinde üç fazlı yol haritası, riskleri minimize eder. Prototip aşamasında, küçük ve izole bir servis (örneğin e-posta bildirim servisi) seçilir. Bu servis, CI/CD boru hattı kurularak, otomatik testlerle doğrulanır. Prototip, ekip için öğrenme fırsatı sunar; hangi araçların işe yaradığını, hangi zorluklarla karşılaşılacağını gösterir. Bu aşama genellikle 4-8 hafta sürer.

Pilot aşamasında, gerçek trafik alan bir servis (örneğin ürün kataloğu veya kullanıcı yönetimi) mikroservis haline getirilir. Canary deployment ile %5-10 trafik yeni servise yönlendirilir. SLA ve SLO metrikleri izlenir; hata oranı, gecikme süreleri ve kaynak kullanımı takip edilir. Pilot başarılı olursa, kalan servisler sırayla dönüştürülür.

Üretim geçişinde, her servis için rollback planı hazırlanır. Feature toggles ile anlık geri alma imkanı sunulur. Veritabanı migrasyonları, veri kaybını önlemek için iki aşamalı yapılır: önce yeni şemaya yazma (write) başlar, sonra okuma (read) yeni şemaya yönlendirilir. Bu süreç, kullanıcı deneyimini etkilemeden sorunsuz geçiş sağlar.

Yol Haritası Zaman Çizelgesi:
Ay 1-2: Prototip servis geliştir ve test et
Ay 3-4: Pilot servisi canary deployment ile yayınla
Ay 5-8: Kalan servisleri sırayla üretime al
Ay 9-12: Monolitik kodu emekliye ayır ve optimizasyon yap

Sonuç

Microservices mimarisi, doğru şekilde uygulandığında, ekiplerin hızını, uygulamaların ölçeklenebilirliğini ve iş sürekliliğini artırır. Ancak bu mimari, her problem için doğru çözüm değildir. Küçük ekipler ve basit uygulamalar için monolitik yapı hâlâ geçerlidir. Microservices'e geçiş kararı, organizasyonun olgunluğu, ekibin yetkinliği ve iş gereksinimleri dikkate alınarak verilmelidir.

Başarılı bir microservices yolculuğu; domain-driven design ile doğru servis sınırları çizmek, API-first yaklaşımı benimsemek, CI/CD ve otomasyonu merkeze almak, observability kültürünü yaygınlaştırmak ve güvenliği her aşamada düşünmekten geçer. Bu makalede ele aldığımız kavramlar ve örnekler, bu yolculuğun ilk adımlarını atmak isteyen geliştirici ve teknik liderler için bir rehber niteliğindedir.

Sektörde microservices projeleri yürüten profesyonel ekiplerde, bu prensiplerin uygulanması, uzun vadede daha sürdürülebilir ve bakımı kolay sistemler inşa etmeyi mümkün kılar. Unutmayın: mimari bir hedef değil, sürekli evrilen bir süreçtir.

Noves Team

Noves Team

Noves Digital: 2020'den beri İzmir merkezli, 3 kişilik tutkulu yazılım ekibi. Web & mobil uygulama, özel yazılım çözümleri. React, Node.js, Python uzmanlığı. Agile çalışma, şeffaf iletişim, %100 zamanında teslimat. Sizin teknoloji partneriniz.