Bu belge, mikroservis mimarisinde yaygın olarak kullanılan 30 tasarım kalıbını açıklamaktadır. Her kalıp için bir açıklama, gerçek dünya senaryosu ve mümkün olduğunda basit bir kod örneği verilmiştir.
1. API Gateway
Açıklama: Tüm istemci isteklerini tek bir giriş noktasından yöneten ve uygun mikroservislere ileten bir ara katman.
Senaryo: Bir e-ticaret uygulamasında, mobil ve web istemcilerinin farklı mikroservislere (ürün, sipariş, kullanıcı vb.) erişmesi gerekiyor.
Örnek (Node.js ile Express kullanarak):
javascriptconst express = require('express'); const httpProxy = require('http-proxy'); const app = express(); const proxy = httpProxy.createProxyServer(); app.use('/products', (req, res) => { proxy.web(req, res, { target: 'http://product-service:3000' }); }); app.use('/orders', (req, res) => { proxy.web(req, res, { target: 'http://order-service:3001' }); }); app.listen(8080, () => console.log('API Gateway başlatıldı'));
2. Service Discovery
Açıklama: Mikroservislerin dinamik olarak birbirlerini bulmasını ve iletişim kurmasını sağlayan mekanizma.
Senaryo: Bir bulut ortamında çalışan mikroservislerin IP adreslerinin sürekli değiştiği bir sistem.
Örnek (Spring Cloud Netflix Eureka kullanarak):
java@SpringBootApplication @EnableEurekaServer public class ServiceRegistryApplication { public static void main(String[] args) { SpringApplication.run(ServiceRegistryApplication.class, args); } }
3. Circuit Breaker
Açıklama: Hatalı bir servise yapılan çağrıları engelleyerek sistemin geri kalanını koruma mekanizması.
Senaryo: Ödeme servisi geçici olarak kullanılamadığında, siparişlerin tamamlanmasını engellemek.
Örnek (Netflix Hystrix kullanarak):
javapublic class PaymentService { @HystrixCommand(fallbackMethod = "fallbackPayment") public boolean processPayment(Order order) { // Ödeme işlemi } public boolean fallbackPayment(Order order) { // Yedek ödeme işlemi veya hata mesajı } }
4. Load Balancer
Açıklama: Gelen istekleri mevcut servis örnekleri arasında dengeli bir şekilde dağıtan sistem.
Senaryo: Yoğun trafiğe sahip bir web uygulamasında, istekleri birden fazla uygulama sunucusuna dağıtmak.
Örnek (NGINX konfigürasyonu):
nginxhttp { upstream backend { server backend1.example.com; server backend2.example.com; server backend3.example.com; } server { listen 80; location / { proxy_pass http://backend; } } }
5. Database per Service
Açıklama: Her mikroservisin kendi veritabanına sahip olması prensibi.
Senaryo: E-ticaret uygulamasında ürün kataloğu ve sipariş yönetimi için ayrı veritabanları kullanmak.
Örnek (Spring Boot uygulaması için konfigürasyon):
yaml# product-service.yml spring: datasource: url: jdbc:mysql://product-db:3306/productdb username: product_user password: product_pass # order-service.yml spring: datasource: url: jdbc:postgresql://order-db:5432/orderdb username: order_user password: order_pass
6. Event Sourcing
Açıklama: Uygulama durumunu bir dizi olay olarak saklamak ve bu olayları yeniden oynatarak durumu yeniden oluşturmak.
Senaryo: Banka hesap hareketlerini her işlemi bir olay olarak kaydederek takip etmek.
Örnek (Basit bir Java uygulaması):
javapublic class BankAccount { private List<Event> events = new ArrayList<>(); private double balance = 0; public void deposit(double amount) { events.add(new DepositEvent(amount)); apply(events.get(events.size() - 1)); } public void withdraw(double amount) { events.add(new WithdrawEvent(amount)); apply(events.get(events.size() - 1)); } private void apply(Event event) { if (event instanceof DepositEvent) { balance += ((DepositEvent) event).getAmount(); } else if (event instanceof WithdrawEvent) { balance -= ((WithdrawEvent) event).getAmount(); } } }
7. CQRS (Command Query Responsibility Segregation)
Açıklama: Veri okuma (query) ve yazma (command) işlemlerini ayrı modellerde gerçekleştirmek.
Senaryo: Yüksek okuma performansı gerektiren bir blog platformu.
Örnek (Basit bir Java uygulaması):
javapublic class BlogService { private WriteRepository writeRepo; private ReadRepository readRepo; public void createPost(Post post) { writeRepo.save(post); // Event bus aracılığıyla okuma modelini güncelle } public Post getPost(String id) { return readRepo.findById(id); } }
8. Saga
Açıklama: Dağıtık işlemleri yönetmek için kullanılan bir dizi yerel işlem.
Senaryo: E-ticaret uygulamasında sipariş oluşturma, ödeme alma ve stok güncelleme işlemlerini koordine etmek.
Örnek (Pseudo-kod):
javapublic class OrderSaga { public void process(Order order) { try { createOrder(order); processPayment(order); updateInventory(order); sendConfirmation(order); } catch (Exception e) { compensate(order); } } private void compensate(Order order) { // Hata durumunda geri alma işlemleri } }
9. Bulkhead
Açıklama: Uygulamayı izole bölümlere ayırarak, bir bölümdeki hatanın diğerlerini etkilemesini önleme.
Senaryo: Çoklu kiracılı (multi-tenant) bir SaaS uygulamasında, bir kiracının yoğun kullanımının diğerlerini etkilememesini sağlamak.
Örnek (Java ile ThreadPoolExecutor kullanarak):
javapublic class BulkheadExample { private Map<String, ExecutorService> tenantExecutors = new HashMap<>(); public void processForTenant(String tenantId, Runnable task) { ExecutorService executor = tenantExecutors.computeIfAbsent(tenantId, k -> new ThreadPoolExecutor(5, 10, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(100))); executor.submit(task); } }
10. Sidecar
Açıklama: Ana uygulamaya ek işlevsellik sağlayan, ancak ana uygulamadan bağımsız çalışan yardımcı servis.
Senaryo: Legacy bir uygulamaya modern bir API Gateway eklemek.
Örnek (Docker Compose ile sidecar konfigürasyonu):
yamlversion: '3' services: legacy-app: image: legacy-app:latest sidecar: image: nginx:latest volumes: - ./nginx.conf:/etc/nginx/nginx.conf ports: - "80:80"
11. Strangler Fig
Açıklama: Eski bir sistemi kademeli olarak yeni bir sistemle değiştirme stratejisi.
Senaryo: Monolitik bir e-ticaret uygulamasını mikroservislere geçirirken, önce ürün kataloğunu ayrı bir servise taşımak.
Örnek (Proxy sunucu konfigürasyonu):
nginxserver { listen 80; server_name example.com; location /products { proxy_pass http://new-product-service; } location / { proxy_pass http://legacy-monolith; } }
12. Backend for Frontend (BFF)
Açıklama: Belirli bir kullanıcı arayüzü veya istemci türü için özelleştirilmiş backend API'si.
Senaryo: Mobil uygulama için optimize edilmiş bir API oluşturmak.
Örnek (Node.js Express uygulaması):
javascriptconst express = require('express'); const app = express(); app.get('/mobile/dashboard', async (req, res) => { const userData = await getUserData(req.user.id); const notifications = await getNotifications(req.user.id); res.json({ user: userData, notifications: notifications.slice(0, 5) // Sadece son 5 bildirim }); }); app.listen(3000, () => console.log('Mobile BFF started'));
13. Externalized Configuration
Açıklama: Uygulama yapılandırmasını koddan ayırarak, dışarıdan yönetilmesini sağlama.
Senaryo: Farklı ortamlar (geliştirme, test, üretim) için farklı veritabanı bağlantı bilgileri kullanmak.
Örnek (Spring Boot uygulaması için application.yml):
yamlspring: profiles: active: ${SPRING_PROFILES_ACTIVE:dev} --- spring: config: activate: on-profile: dev datasource: url: jdbc:mysql://localhost:3306/devdb username: devuser password: devpass --- spring: config: activate: on-profile: prod datasource: url: jdbc:mysql://prod-db-server:3306/proddb username: ${DB_USERNAME} password: ${DB_PASSWORD}
14. Service Mesh
Açıklama: Mikroservisler arasındaki iletişimi yöneten, güvenliği sağlayan ve izleme yapan altyapı katmanı.
Senaryo: Büyük ölçekli bir mikroservis uygulamasında, servisler arası iletişimi yönetmek ve izlemek.
Örnek (Istio service mesh konfigürasyonu):
yamlapiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: reviews-route spec: hosts: - reviews.prod.svc.cluster.local http: - route: - destination: host: reviews.prod.svc.cluster.local subset: v1 weight: 75 - destination: host: reviews.prod.svc.cluster.local subset: v2 weight: 25
15. Event-Driven Architecture
Açıklama: Sistemdeki bileşenlerin birbirleriyle olaylar aracılığıyla iletişim kurduğu mimari.
Senaryo: Bir e-ticaret sisteminde, sipariş oluşturulduğunda stok yönetimi, faturalama ve kargo servislerinin bilgilendirilmesi.
Örnek (Java ile Apache Kafka kullanarak):
java@KafkaListener(topics = "order-created") public void handleOrderCreated(OrderCreatedEvent event) { // Sipariş oluşturuldu olayını işle updateInventory(event.getOrderItems()); createInvoice(event.getOrderId()); initiateShipment(event.getOrderId()); }
16. Health Check API
Açıklama: Servisin durumunu kontrol etmek için bir API endpoint'i sağlama.
Senaryo: Bir load balancer'ın hangi servis örneklerinin sağlıklı olduğunu belirlemesi.
Örnek (Spring Boot ile):
java@RestController public class HealthController { @GetMapping("/health") public ResponseEntity<String> healthCheck() { // Veritabanı bağlantısı, bağımlılıklar vb. kontrol edilebilir return ResponseEntity.ok("UP"); } }
17. Log Aggregation
Açıklama: Farklı mikroservislerden gelen logları merkezi bir sistemde toplama ve analiz etme.
Senaryo: Dağıtık bir sistemde hata ayıklama ve performans izleme.
Örnek (Docker Compose ile ELK stack kullanımı):
yamlversion: '3' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.10.0 logstash: image: docker.elastic.co/logstash/logstash:7.10.0 volumes: - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf kibana: image: docker.elastic.co/kibana/kibana:7.10.0 ports: - "5601:5601"
18. Distributed Tracing
Açıklama: Bir isteğin mikroservisler arasındaki yolculuğunu izleme ve analiz etme.
Senaryo: Karmaşık bir işlem akışında performans darboğazlarını tespit etmek.
Örnek (Java ile Spring Cloud Sleuth ve Zipkin kullanarak):
java@SpringBootApplication @EnableZipkinServer public class ZipkinServerApplication { public static void main(String[] args) { SpringApplication.run(ZipkinServerApplication.class, args); } } // Mikroservis uygulamasında @Autowired private Tracer tracer; public void processOrder(Order order) { Span span = tracer.nextSpan().name("process-order").start(); try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { // İşlem adımları } finally { span.finish(); } }
19. Blue-Green Deployment
Açıklama: Yeni sürümü (blue) canlı ortama almadan önce mevcut sürümle (green) yan yana çalıştırma stratejisi.
Senaryo: Bir e-ticaret sitesinin yeni sürümünü risk almadan yayına almak.
Örnek (Kubernetes ile):
yamlapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app-ingress annotations: kubernetes.io/ingress.class: nginx spec: rules: - host: myapp.example.com http: paths: - path: / pathType: Prefix backend: service: name: my-app-green port: number: 80 --- # Yeni sürüm hazır olduğunda apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app-ingress annotations: kubernetes.io/ingress.class: nginx spec: rules: - host: myapp.example.com http: paths: - path: / pathType: Prefix backend: service: name: my-app-blue port: number: 80
20. Feature Toggle
Açıklama: Uygulama özelliklerini dinamik olarak açıp kapatma mekanizması.
Senaryo: Yeni bir özelliği sadece beta kullanıcılarına açmak.
Örnek (Java ile Togglz kütüphanesi kullanarak):
javapublic enum Features implements Feature { @Label("New Payment Gateway") NEW_PAYMENT_GATEWAY; public boolean isActive() { return FeatureContext.getFeatureManager().isActive(this); } } @RestController public class PaymentController { @PostMapping("/process-payment") public ResponseEntity<?> processPayment(@RequestBody PaymentRequest request) { if (Features.NEW_PAYMENT_GATEWAY.isActive()) { return processWithNewGateway(request); } else { return processWithOldGateway(request); } } }
21. Consumer-Driven Contract Testing
Açıklama: Servis tüketicilerinin beklentilerine göre servis sağlayıcıların test edilmesi.
Senaryo: Bir ödeme servisinin, e-ticaret uygulamasının beklentilerine uygun çalıştığından emin olmak.
Örnek (Java ile Spring Cloud Contract kullanarak):
groovy// src/test/resources/contracts/shouldProcessPayment.groovy Contract.make { request { method 'POST' url '/payments' body([ amount: 100.00, currency: 'USD' ]) headers { contentType('application/json') } } response { status 200 body([ id: anyUuid(), status: 'PROCESSED' ]) headers { contentType('application/json') } } }
22. API Versioning
Açıklama: API'lerin farklı sürümlerini yönetme ve geriye dönük uyumluluğu koruma stratejisi.
Senaryo: Mevcut müşterileri etkilemeden bir API'yi güncellemek.
Örnek (Spring Boot ile URL tabanlı versiyonlama):
java@RestController @RequestMapping("/api/v1/users") public class UserControllerV1 { @GetMapping("/{id}") public UserV1 getUserV1(@PathVariable Long id) { // V1 kullanıcı verisi döndür } } @RestController @RequestMapping("/api/v2/users") public class UserControllerV2 { @GetMapping("/{id}") public UserV2 getUserV2(@PathVariable Long id) { // V2 kullanıcı verisi döndür } }
23. Backends for Frontends (BFF)
Açıklama: Her farklı istemci türü için özelleştirilmiş backend API'leri oluşturma.
Senaryo: Mobil uygulama, web uygulaması ve IoT cihazları için farklı API'ler sunmak.
Örnek (Node.js Express ile):
javascript// Mobile BFF app.get('/mobile/dashboard', (req, res) => { // Mobil için optimize edilmiş dashboard verisi }); // Web BFF app.get('/web/dashboard', (req, res) => { // Web için tam kapsamlı dashboard verisi }); // IoT BFF app.get('/iot/status', (req, res) => { // IoT cihazları için minimal durum verisi });
24. Throttling
Açıklama: Bir servise yapılan istekleri sınırlayarak aşırı yüklenmeyi önleme.
Senaryo: Ücretsiz API kullanıcılarının dakikada yapabileceği istek sayısını sınırlamak.
Örnek (Express.js ile rate-limiter-flexible kullanarak):
javascriptconst express = require('express'); const { RateLimiterMemory } = require('rate-limiter-flexible'); const app = express(); const rateLimiter = new RateLimiterMemory({ points: 10, // 10 istek duration: 60 // 60 saniye içinde }); app.use((req, res, next) => { rateLimiter.consume(req.ip) .then(() => { next(); }) .catch(() => { res.status(429).send('Too Many Requests'); }); });
25. CQRS (Command Query Responsibility Segregation)
Açıklama: Veri okuma (query) ve yazma (command) işlemlerini ayrı modellerde gerçekleştirme.
Senaryo: Yüksek okuma performansı gerektiren bir blog platformu.
Örnek (C# ile):
csharppublic class BlogService { private readonly ICommandRepository _commandRepo; private readonly IQueryRepository _queryRepo; public BlogService(ICommandRepository commandRepo, IQueryRepository queryRepo) { _commandRepo = commandRepo; _queryRepo = queryRepo; } public async Task CreatePost(CreatePostCommand command) { await _commandRepo.CreatePost(command); // Event bus aracılığıyla okuma modelini güncelle } public async Task<PostDto> GetPost(int id) { return await _queryRepo.GetPostById(id); } }
26. Event Sourcing
Açıklama: Uygulama durumunu bir dizi olay olarak saklama ve bu olayları yeniden oynatarak durumu yeniden oluşturma.
Senaryo: Banka hesap hareketlerini her işlemi bir olay olarak kaydederek takip etmek.
Örnek (C# ile):
csharppublic class BankAccount { private List<Event> _events = new List<Event>(); public decimal Balance { get; private set; } public void Deposit(decimal amount) { var @event = new DepositEvent(amount); Apply(@event); _events.Add(@event); } public void Withdraw(decimal amount) { var @event = new WithdrawEvent(amount); Apply(@event); _events.Add(@event); } private void Apply(Event @event) { switch (@event) { case DepositEvent depositEvent: Balance += depositEvent.Amount; break; case WithdrawEvent withdrawEvent: Balance -= withdrawEvent.Amount; break; } } public void Replay(IEnumerable<Event> events) { foreach (var @event in events) { Apply(@event); } } }
27. Saga Pattern
Açıklama: Dağıtık işlemleri yönetmek için kullanılan uzun ömürlü işlem modeli.
Senaryo: E-ticaret uygulamasında sipariş oluşturma, ödeme alma ve stok güncelleme işlemlerini koordine etmek.
Örnek (Pseudo-kod):
javapublic class OrderSaga { public void process(Order order) { try { createOrder(order); processPayment(order); updateInventory(order); sendConfirmation(order); } catch (Exception e) { compensate(order); } } private void compensate(Order order) { // Hata durumunda geri alma işlemleri if (order.isCreated()) cancelOrder(order); if (order.isPaid()) refundPayment(order); if (order.isInventoryUpdated()) restoreInventory(order); } }
28. Sidecar Pattern
Açıklama: Ana uygulamaya ek işlevsellik sağlayan, ancak ana uygulamadan bağımsız çalışan yardımcı servis.
Senaryo: Legacy bir uygulamaya modern bir API Gateway eklemek.
Örnek (Docker Compose ile):
yamlversion: '3' services: legacy-app: image: legacy-app:latest ports: - "8080:8080" sidecar: image: nginx:latest volumes: - ./nginx.conf:/etc/nginx/nginx.conf ports: - "80:80" depends_on: - legacy-app
29. Strangler Fig Pattern
Açıklama: Eski bir sistemi kademeli olarak yeni bir sistemle değiştirme stratejisi.
Senaryo: Monolitik bir e-ticaret uygulamasını mikroservislere geçirirken, önce ürün kataloğunu ayrı bir servise taşımak.
Örnek (Nginx konfigürasyonu):
nginxhttp { upstream legacy_app { server legacy_app:8080; } upstream product_service { server product_service:8081; } server { listen 80; server_name example.com; location /products { proxy_pass http://product_service; } location / { proxy_pass http://legacy_app; } } }
30. Bulkhead Pattern
Açıklama: Uygulamayı izole bölümlere ayırarak, bir bölümdeki hatanın diğerlerini etkilemesini önleme.
Senaryo: Çoklu kiracılı (multi-tenant) bir SaaS uygulamasında, bir kiracının yoğun kullanımının diğerlerini etkilememesini sağlamak.
Örnek (Java ile ThreadPoolExecutor kullanarak):
javapublic class BulkheadExample { private Map<String, ExecutorService> tenantExecutors = new HashMap<>(); public void processForTenant(String tenantId, Runnable task) { ExecutorService executor = tenantExecutors.computeIfAbsent(tenantId, k -> new ThreadPoolExecutor(5, 10, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(100))); executor.submit(task); } public void shutdownTenant(String tenantId) { ExecutorService executor = tenantExecutors.remove(tenantId); if (executor != null) { executor.shutdown(); } } }
Bu 30 mikroservis tasarım kalıbı, modern dağıtık sistemlerin karşılaştığı çeşitli zorlukları ele almaktadır. Her kalıp, belirli bir problemi çözmek için tasarlanmıştır ve uygun şekilde uygulandığında, mikroservis mimarisinin esneklik, ölçeklenebilirlik ve dayanıklılık gibi temel faydalarını artırabilir.
Junior geliştiriciler için önemli olan, bu kalıpları anlamak, hangi durumlarda kullanılacaklarını bilmek ve projenin gereksinimlerine göre uygun kalıpları seçebilmektir. Ayrıca, bu kalıpların her birinin kendi karmaşıklıkları ve trade-off'ları olduğunu unutmamak önemlidir. Bu nedenle, bir kalıbı uygulamadan önce, o kalıbın getireceği faydaları ve olası dezavantajları dikkatle değerlendirmek gerekir.