MİMARİ14m READ10 Haziran 2026

Microservices Mimarisi: API Gateway ve Servisler Arası İletişim

Circuit Breaker pattern, Message Queue ve distributed tracing.

Microservices mimarisi, büyük uygulamaları bağımsız deploy edilebilen küçük servislere bölme yaklaşımıdır. Doğru kullanıldığında ölçeklenebilirlik ve geliştirme hızı kazandırır; yanlış uygulandığında ise "dağıtık monolit" yaratır.

Monolith vs Microservices

ÖzellikMonolithMicroservices
Başlangıç karmaşıklığıDüşükYüksek
Deploy bağımsızlığıHayırEvet
Teknoloji çeşitliliğiKısıtlıTam özgürlük
Debug kolaylığıBasitDağıtık tracing gerekli
ÖlçeklenebilirlikTüm uygulamaServis bazlı
Takım bağımsızlığıZorİdeal
Veri yönetimiTek DBHer servis kendi DB

Servis Tasarımı

// PLAINTEXT //
blog-platformu/
├── api-gateway/          → Tek giriş noktası
├── auth-service/         → JWT, OAuth, kullanıcı yönetimi
├── makale-service/       → Makaleler, taslaklar, slug
├── yorum-service/        → Yorumlar, moderasyon
├── bildirim-service/     → E-posta, push, in-app
├── arama-service/        → Elasticsearch entegrasyonu
└── medya-service/        → Dosya yükleme, CDN, resim işleme

Her servis:

  • Kendi veritabanına sahip (database per service)
  • Bağımsız deploy edilebilir
  • Tek bir business domain'i kapsar

API Gateway

// TYPESCRIPT //
// api-gateway/src/index.ts
// http-proxy-middleware ile basit gateway
import express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';
import rateLimit from 'express-rate-limit';
import helmet from 'helmet';
 
const app = express();
app.use(helmet());
 
// Global rate limit
app.use(rateLimit({ windowMs: 60_000, max: 100 }));
 
// Servis yönlendirmesi
const SERVISLER = {
  AUTH:     process.env.AUTH_SERVICE_URL     || 'http://auth-service:3001',
  MAKALE:   process.env.MAKALE_SERVICE_URL   || 'http://makale-service:3002',
  YORUM:    process.env.YORUM_SERVICE_URL    || 'http://yorum-service:3003',
  ARAMA:    process.env.ARAMA_SERVICE_URL    || 'http://arama-service:3004',
  MEDYA:    process.env.MEDYA_SERVICE_URL    || 'http://medya-service:3005',
};
 
// Token doğrulama middleware
async function tokenDogrula(req: express.Request, res: express.Response, next: express.NextFunction) {
  const token = req.headers.authorization?.replace('Bearer ', '');
  if (!token) {
    if (req.path.startsWith('/api/auth')) return next(); // Auth rotaları hariç
    return res.status(401).json({ hata: 'Token gerekli.' });
  }
 
  try {
    // Auth servisten token doğrula
    const cevap = await fetch(`${SERVISLER.AUTH}/internal/verify`, {
      headers: { Authorization: `Bearer ${token}` },
    });
 
    if (!cevap.ok) return res.status(401).json({ hata: 'Geçersiz token.' });
 
    const payload = await cevap.json();
    req.headers['x-user-id']  = payload.userId;
    req.headers['x-user-rol'] = payload.rol;
    next();
  } catch {
    res.status(503).json({ hata: 'Auth servisi erişilemiyor.' });
  }
}
 
app.use('/api/auth', createProxyMiddleware({ target: SERVISLER.AUTH, changeOrigin: true }));
app.use('/api/makaleler', tokenDogrula, createProxyMiddleware({ target: SERVISLER.MAKALE, changeOrigin: true }));
app.use('/api/yorumlar',  tokenDogrula, createProxyMiddleware({ target: SERVISLER.YORUM,  changeOrigin: true }));
app.use('/api/arama',     createProxyMiddleware({ target: SERVISLER.ARAMA,  changeOrigin: true }));
app.use('/api/medya',     tokenDogrula, createProxyMiddleware({ target: SERVISLER.MEDYA,  changeOrigin: true }));
 
app.listen(3000, () => console.log('API Gateway :3000'));

Servisler Arası İletişim

// TYPESCRIPT //
// Senkron — HTTP (basit ama bağımlılık yaratır)
async function yorumSayisiniGetir(makaleId: string): Promise<number> {
  const res = await fetch(`${YORUM_SERVICE}/internal/makaleler/${makaleId}/sayi`, {
    headers: { 'X-Internal-Secret': process.env.INTERNAL_SECRET! },
  });
  if (!res.ok) return 0; // Circuit breaker pattern
  const { sayi } = await res.json();
  return sayi;
}
 
// Asenkron — Message Queue (gevşek bağlantı)
// makale-service — yayınla
import { Channel } from 'amqplib';
 
async function makaleYayinlandi(kanallar: Channel, makale: Makale) {
  const olay = {
    tur:   'MAKALE_YAYINLANDI',
    zaman: new Date().toISOString(),
    veri:  { makaleId: makale.id, baslik: makale.baslik, yazarId: makale.yazarId },
  };
  kanallar.publish('makale-olaylari', 'makale.yayinlandi', Buffer.from(JSON.stringify(olay)));
}
 
// bildirim-service — dinle
kanallar.consume('makale-yayinlandi-kuyrugu', async (msg) => {
  if (!msg) return;
  const olay = JSON.parse(msg.content.toString());
  await yazaraTakipcilereBildirimGonder(olay.veri);
  kanallar.ack(msg);
});

Circuit Breaker Pattern

// TYPESCRIPT //
// Arıza olduğunda sürekli istek atmayı engelle
class CircuitBreaker {
  private hataSayisi  = 0;
  private sonHata:    Date | null = null;
  private readonly esik       = 5;
  private readonly zaman_asimi = 60_000;
 
  get durum(): 'kapali' | 'acik' | 'yari-acik' {
    if (this.hataSayisi < this.esik) return 'kapali';
    if (Date.now() - this.sonHata!.getTime() > this.zaman_asimi) return 'yari-acik';
    return 'acik';
  }
 
  async calistir<T>(fn: () => Promise<T>): Promise<T> {
    if (this.durum === 'acik') {
      throw new Error('Servis geçici olarak kullanılamıyor (circuit open).');
    }
 
    try {
      const sonuc = await fn();
      if (this.durum === 'yari-acik') {
        this.hataSayisi = 0; // Başarılıysa sıfırla
      }
      return sonuc;
    } catch (hata) {
      this.hataSayisi++;
      this.sonHata = new Date();
      throw hata;
    }
  }
}
 
const yorumServisBreaker = new CircuitBreaker();
 
async function yorumlariGetir(makaleId: string) {
  return yorumServisBreaker.calistir(() =>
    fetch(`${YORUM_SERVICE}/makaleler/${makaleId}/yorumlar`).then(r => r.json())
  );
}

Docker Compose ile Yerel Ortam

// YAML //
# docker-compose.yml
services:
  api-gateway:
    build: ./api-gateway
    ports: ["3000:3000"]
    environment:
      AUTH_SERVICE_URL:   http://auth-service:3001
      MAKALE_SERVICE_URL: http://makale-service:3002
    depends_on: [auth-service, makale-service]
 
  auth-service:
    build: ./auth-service
    environment:
      DATABASE_URL: postgresql://dev:dev@auth-db:5432/authdb
      JWT_SECRET:   ${JWT_SECRET}
    depends_on: [auth-db]
 
  makale-service:
    build: ./makale-service
    environment:
      DATABASE_URL: postgresql://dev:dev@makale-db:5432/makaledb
      RABBITMQ_URL: amqp://rabbit:5672
    depends_on: [makale-db, rabbit]
 
  auth-db:
    image: postgres:16-alpine
    environment: { POSTGRES_DB: authdb, POSTGRES_USER: dev, POSTGRES_PASSWORD: dev }
    volumes: [auth-data:/var/lib/postgresql/data]
 
  makale-db:
    image: postgres:16-alpine
    environment: { POSTGRES_DB: makaledb, POSTGRES_USER: dev, POSTGRES_PASSWORD: dev }
    volumes: [makale-data:/var/lib/postgresql/data]
 
  rabbit:
    image: rabbitmq:3-management-alpine
    ports: ["15672:15672"]
 
volumes:
  auth-data:
  makale-data:

Distributed Tracing

// TYPESCRIPT //
// OpenTelemetry ile her servise trace
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
 
const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
  }),
  serviceName: 'makale-service',
});
sdk.start();
 
// Her request otomatik trace'lenir
// Jaeger/Zipkin UI'da tüm servis çağrıları görünür:
// [api-gateway] → [makale-service] → [yorum-service]
//                                  ↘ [auth-service]

Sonuç

Microservices, takım bağımsızlığı ve servis bazlı ölçeklendirme sunar; ancak distributed systems karmaşıklığını beraberinde getirir (network hataları, eventual consistency, distributed tracing). API Gateway tek giriş noktası sağlar; Circuit Breaker arıza yönetimini kolaylaştırır; Message Queue servisleri gevşek bağlı tutar. Her büyük özellik doğru servisin olması kadar, yanlış servisi oluşturmamak da önemlidir.