Laravel'de Mikroservis Mimarisi: Ne Zaman Geçmeli, Ne Zaman Uzak Durmalı?
Monolitik Laravel uygulamanız büyüdükçe "bunu mikroservislere bölsek mi?" sorusu kaçınılmaz oluyor. Ben de bu soruyu birkaç projede sordum, birinde geçiş yaptım, birinde vazgeçtim. Bu yazıda hem teknik detayları hem de pratikte karşılaştığım şeyleri paylaşacağım.
Önce Bir Adım Geri: Mikroservis Ne Demek?
Klasik monolitik yapıda her şey tek bir Laravel projesinde yaşar — auth, ödeme, bildirim, raporlama hepsi aynı codebase'de. Mikroserviste ise bunların her biri ayrı bir uygulama. Kendi veritabanı, kendi deploy süreci, kendi repository'si var.
Bunu şöyle düşünün: monolitik yapı bir apartman dairesi. Her şey bir çatı altında, bir kapıdan giriyorsunuz. Mikroservis ise müstakil evlerden oluşan bir site. Her ev bağımsız ama aynı altyapıyı (yol, elektrik, su) paylaşıyorlar.
Monolitik vs Mikroservis: Dürüst Bir Karşılaştırma
Şunu baştan söyleyeyim: mikroservis her zaman doğru cevap değil. Hatta çoğu proje için yanlış cevap.
Monolitik yapı şu durumlarda yeterli:
- Ekibiniz 3-5 kişi
- Tek bir domain üzerinde çalışıyorsunuz
- Aylık kullanıcı sayınız onbinler seviyesinde
- Deploy sıklığınız haftada 1-2
Mikroservis şu durumlarda mantıklı:
- Farklı ekipler farklı özellikleri bağımsız geliştiriyor
- Bir bileşenin çökmesi diğerlerini etkilememeli
- Farklı bileşenler farklı oranlarda ölçeklenmeli (ödeme servisi vs profil servisi)
- Deploy frekansınız günde birkaç kez
Pratikte gördüğüm en büyük hata: 3 kişilik ekiple, henüz product-market fit bulamadan mikroservis mimarisine geçmeye çalışmak. Gereksiz karmaşıklık, gereksiz altyapı maliyeti.
Laravel Bu İş İçin Uygun Mu?
Kısaca: evet, ama dikkatli olmak lazım.
Laravel monolitik framework olarak tasarlandı. Ama birkaç özelliği mikroservis geliştirmeyi kolaylaştırıyor:
Eloquent ORM — Her servisin kendi veritabanı bağlantısı olabilir. config/database.php'de birden fazla connection tanımlayıp model bazında ayırabilirsiniz.
Queue sistemi — Servisler arası asenkron iletişimin bel kemiği. Redis veya RabbitMQ üzerinden event'ler fırlatıp diğer servislerin dinlemesini sağlayabilirsiniz.
HTTP Client — Laravel 7'den beri gelen HTTP facade'ı, servisler arası REST çağrıları için gayet yeterli:
$response = Http::withToken($apiToken)
->timeout(5)
->post('http://payment-service/api/charge', [
'amount' => 150.00,
'currency' => 'TRY',
]);
API Resource'lar — Servisler arası veri formatını standartlaştırmak için biçilmiş kaftan.
Bir de Lumen vardı — Laravel'in hafif versiyonu. Ama Laravel 10'dan sonra Lumen artık geliştirilmiyor. Bunun yerine Laravel'i gereksiz paketler olmadan kurup hafifletmek daha mantıklı.
Pratikte Nasıl Yapılıyor?
Diyelim ki bir e-ticaret projeniz var ve mikroservislere geçmek istiyorsunuz. İşte benim izlediğim adımlar:
1. Domain'leri belirle
Önce "neyi ayıracağız?" sorusunu cevaplayın. DDD (Domain-Driven Design) burada işe yarıyor. E-ticaret örneğinde:
- Sipariş Servisi: Sipariş oluşturma, durum takibi, iptal
- Ödeme Servisi: Ödeme alma, iade, mutabakat
- Ürün Servisi: Katalog yönetimi, stok takibi
- Kullanıcı Servisi: Auth, profil, adres yönetimi
- Bildirim Servisi: E-posta, SMS, push notification
2. İletişim yöntemini seç
İki temel yaklaşım var:
Senkron (HTTP/REST): Bir servis diğerine HTTP request atar, cevap bekler. Basit ama tehlikeli — çağırdığınız servis çökerse siz de çökersiniz.
Asenkron (Event/Queue): Bir servis event fırlatır, ilgilenen servisler dinler. Daha dayanıklı ama daha karmaşık. Eventual consistency'i kabul etmeniz gerekiyor.
Benim tercihim: kritik akışlarda (ödeme onayı gibi) senkron, geri kalanında asenkron.
3. Veritabanı stratejisi
Her servisin kendi veritabanı olmalı — buna "database per service" deniyor. Paylaşılan veritabanı kullanırsanız servisler arasında tight coupling oluşur ve mikroservisin tüm amacını kaybedersiniz.
Ama bu beraberinde sorunları getiriyor: distributed transaction yok, veri tutarlılığını kendiniz yönetmelisiniz. Saga pattern burada devreye giriyor ama implementasyonu kolay değil.
4. Docker ile paketleme
Her servisi Docker container'ına koyun. Dockerfile + docker-compose.yml ile development ortamında tüm servisleri ayağa kaldırabilirsiniz:
services:
order-service:
build: ./order-service
ports:
- "8001:80"
depends_on:
- mysql-orders
- redis
payment-service:
build: ./payment-service
ports:
- "8002:80"
depends_on:
- mysql-payments
- redis
redis:
image: redis:alpine
5. API Gateway koy
Client'ın her servisle ayrı ayrı konuşması yerine tek bir giriş noktası olsun. Kong, Traefik veya basit bir Nginx reverse proxy işi görür.
Dikkat Etmeniz Gerekenler
Birkaç yıllık deneyimden süzülmüş notlar:
Monitoring şart. Monolitikte tek bir log dosyasına bakıyordunuz. Mikroserviste 5 farklı servisin logunu takip etmeniz gerekiyor. ELK stack veya Grafana+Loki olmadan yapmayın.
Network güvenilmez. Servisler arası çağrılarda timeout, retry ve circuit breaker pattern mutlaka olmalı. Bir servis yavaşladığında diğerlerini de yavaşlatmasın.
Deploy pipeline'ı karmaşıklaşır. Tek git push yerine her servis için ayrı CI/CD pipeline gerekiyor. GitHub Actions veya GitLab CI ile her servisin kendi pipeline'ı olsun.
Lokal development zorlaşır. 5 servisi aynı anda ayağa kaldırmak, hepsinin birbiriyle konuştuğundan emin olmak... Docker Compose bu işin olmazsa olmazı.
Veri tutarlılığı zor. İki servis arasında transaction yapamazsınız. Event-driven yaklaşımda "eventual consistency" kabul etmeniz gerekiyor. Müşteriye "siparişiniz alındı" dersiniz ama ödeme servisi henüz onaylamamış olabilir.
Benim Tavsiyem
Çoğu proje için şu yol haritasını öneriyorum:
-
Monolitik başlayın. Laravel'in modüler yapısı (Service class'lar, Repository pattern, hatta nwidart/laravel-modules) ile kodu iyi organize edin.
-
Darboğazı tespit edin. Gerçekten hangi bileşen bağımsız ölçeklenmeli? Profil sayfası mı yoksa ödeme sistemi mi? Veri olmadan bu kararı vermeyin.
-
Kademeli geçiş yapın. Tüm sistemi bir anda söküp yeniden yazmayın. Önce en bağımsız, en çok ölçeklenmesi gereken parçayı ayırın. Strangler Fig Pattern buna çok uygun.
-
Altyapı hazırlığınızı tamamlayın. Docker, CI/CD, monitoring, centralized logging — bunlar olmadan mikroservise geçmeyin.
Kısacası: mikroservis bir araç, amaç değil. Probleminiz monolitik yapıda çözülemiyorsa geçin, çözülüyorsa geçmeyin. Basit tutun.