Architecture

Mikroservisler: Ne Zaman Değer, Ne Zaman Pahalı Bir Hata

Çoğu ekibin mikroservislerle başlamaması gerekir. Ekip büyüklüğü, Conway Yasası, saga pattern, service mesh ve modüler monolit orta yolu üzerine pratik bir rehber.

Yazan IWWOMI
· 12 dk okuma
Mikroservisler: Ne Zaman Değer, Ne Zaman Pahalı Bir Hata

Mikroservisler, 2016 civarında “bunu nasıl inşa edelim?” sorusunun varsayılan cevabı haline geldi ve sektör o günden beri sessizce bu kararın faturasını ödüyor. IWWOMI’de yeterince mimari incelemesi yaptık ve şunu açıkça söyleyebiliriz: 20’den az mühendisiniz varsa, mikroservisler büyük olasılıkla yanlış seçimdir. Yazının geri kalanı bunun nedenini ve hesabın ne zaman gerçekten döndüğünü anlatıyor.

”Majestic Monolith” Her Zaman Doğru Varsayılandı

DHH 2016’da “majestic monolith” terimini kullandı ve bunun için alay konusu oldu. Haklıydı. Basecamp, Shopify, Stack Overflow, GitHub — hepsi konferans konuşmacılarının “artık bölmeniz lazım” dediği noktanın çok ötesine kadar devasa monolitler işletti (bazıları hâlâ işletiyor). Sebep göz alıcı değil: tek bir deploy edilebilir artefakt, bir servis grafiğine kıyasla anlamak için dramatik biçimde daha ucuzdur.

Monolitte fonksiyon çağrısı sadece bir fonksiyon çağrısıdır. Mikroservislerde aynı çağrı; retry’lar, timeout’lar, circuit breaker’lar, serialization, version skew ve gece 2’de okumak zorunda kalacağınız bir distributed trace içeren bir ağ hopuna dönüşür. Bu karmaşıklığın maliyeti sabittir; faydası ancak belli bir ölçekte ortaya çıkar.

Ekibiniz tek bir Slack kanalına sığıp hâlâ iş çıkarabiliyorsa, mimari probleminiz yok. Disiplin probleminiz var. Mikroservisler bunu daha da kötüleştirir, düzeltmez.

Monolitle başlayın. Modüler tutun. “Modüler” derken kod düzeyinde ne kastettiğimizi aşağıda anlatacağız.

Conway Yasası Bir Öneri Değil

Melvin Conway 1967’de şunu gözlemledi: “organizasyonlar, kendi iletişim yapılarının kopyası olan tasarımlar üretir.” Altmış yıl sonra bu hâlâ yazılım mimarisindeki en az takdir edilen kuvvet.

Servis sınırlarınız sonunda ekip sınırlarınızla örtüşecek. Üç ekibiniz varsa, domain modeliniz ne derse desin, sonunda üç servisiniz olacak. Buna karşı savaşırsanız servisler sorumluluk sızdıracak, on-call rotasyonları acı verici olacak ve PR’lar ekip sınırlarında birikecek.

Bu yüzden “mikroservis kullanmalı mıyız?” sorusu aslında “org şemamız nasıl görünüyor ve 18 ay sonra nasıl görünmesini istiyoruz?” sorusudur. Org şemasından bağımsız alınmış mimari kararlar, org şeması tarafından bozulur.

Pratik bir kural:

  • 1–2 ekip (~15 mühendisten az): tek monolit, nokta.
  • 3–6 ekip (~15–30 mühendis): modüler monolit, belki gerçekten farklı ölçek profili olan bir iki çıkarılmış servis.
  • 6+ ekip (30+ mühendis): DevOps olgunluğu varsa mikroservisler kendini ödemeye başlar.

Çoğu şirket ortadaki aralıkta yaşar ve çoğu mimari hata burada yapılır.

Mikroservisler Gerçekten Ne Zaman Karşılığını Verir

Üç meşru tetikleyici var. En az biri sizde yoksa, cargo cult yapıyorsunuz.

1. Ekip büyüklüğü ve deploy sürtünmesi. ~30’dan fazla mühendis aynı kod tabanına commit attığında; merge çakışmaları, CI kuyruğu süreleri ve release koordinasyonu gerçek saatleri yemeye başlar. Deploy edilebilir birimleri ekip sınırlarına göre bölmek bu zamanı geri kazandırır.

2. Gerçekten farklı ölçek profilleri. Video transcoding iş yükünüzle billing iş yükünüzün operasyonel olarak hiçbir ortak noktası yoktur. Biri GPU ve burst kapasitesi ister, diğeri güçlü tutarlılık ve audit log ister. Aynı deployable’a sıkıştırmak birini over-provision, diğerini under-protect etmek demektir.

3. Risk için deploy izolasyonu. Haftada bir, dört göz incelemeyle çıkan ödeme kodu, günde on beş kez çıkan pazarlama CMS’iyle aynı deploy’u paylaşmamalı. Bunları ayırmak hem regülasyon hem güvenilirlik kazancıdır.

Sebebiniz “bazı şeyleri Go’da, bazılarını Python’da istiyoruz” veya “mimari diyagram daha temiz görünüyor” ise — oturun. Bunlar sebep değil.

Pitch Deck’te Anlatılmayan Gizli Maliyetler

Her mikroservis önerisi operasyonel yüzey alanını eksik anlatır. Gerçek fatura şu.

Distributed tracing opsiyonel değil. Üç servisiniz olduğu an OpenTelemetry instrumentation, bir trace backend (Jaeger, Tempo, Honeycomb) ve flame graph okumayı bilen mühendislere ihtiyacınız var. Bu olmadan 500ms latency regresyonunu debug etmek arkeoloji haline gelir.

Ağ güvenilirliği artık sizin probleminiz. TCP yeterince güvenilir değil. Exponential backoff’lu retry’lara, idempotency key’lere, circuit breaker’lara ve her çağrı yeri için ayarlanmış timeout’lara ihtiyacınız var. Her biri yanlış konfigüre edildiğinde ayağınıza sıkacağınız bir silah. Timeout’u çok düşük tutarsanız cascade failure, çok yüksek tutarsanız connection pool tükenmesi.

Transaction sınırları kayboluyor. Büyük olan bu. Monolitte BEGIN; ... COMMIT; size atomikliği bedavaya verir. Servisler arasında saga pattern’e geçersiniz — ve saga’lar zordur.

Saga Pattern, Somut Olarak

Diyelim bir sipariş işliyorsunuz: stok rezerve et, ödeme al, kargo oluştur. Monolitte:

with db.transaction():
    inventory.reserve(order)
    payment.charge(order)
    shipment.create(order)

Üç servis demek üç veritabanı demek; paylaşılan transaction yok. Saga gerekir — geri alma için telafi edici aksiyonları olan yerel transaction’lar dizisi:

# Orchestrator tabanlı saga
async def place_order_saga(order):
    try:
        reservation = await inventory_service.reserve(order)
        try:
            charge = await payment_service.charge(order)
            try:
                await shipment_service.create(order)
            except Exception:
                await payment_service.refund(charge.id)
                raise
        except Exception:
            await inventory_service.release(reservation.id)
            raise
    except Exception as e:
        await order_service.mark_failed(order.id, reason=str(e))
        raise

Bu basit hali. Gerçek hali; telafi aksiyonlarının kendi kısmi başarısızlıklarını, mükerrer event’leri ve payment.refund başarılı ama ağ çağrısı başarısız senaryosunu yönetir. Outbox tablosu, idempotent handler’lar ve saga’ları herhangi bir noktadan replay etme mekanizması gerekir. Monolitinizde bunların hiçbiri yok çünkü hiç ihtiyacınız olmadı.

Domain’inizde doğal telafi aksiyonları yoksa — “gönderilmiş maili geri al,” “hesaplanmış vergiyi geri hesapla” — saga’lar hem felsefi hem pratik olarak acı verici hale gelir. Servisleri böyle bir sınırdan ayırmadan önce iyi düşünün.

Service Mesh: Neredeyse Her Zaman Erken

Istio, Linkerd ve Consul Connect gerçek problemler çözer: servisler arası mTLS, traffic splitting, platform katmanında retry ve timeout, servis başına instrumentation gerektirmeyen observability. Aynı zamanda her pod’a bir sidecar, işletilecek bir control plane ve birinin yılının dörtte birini yiyecek bir öğrenme eğrisi eklerler.

Genel kural: 50 servisten azsa service mesh’e ihtiyacınız yok. İhtiyacınız olan bir API gateway (Kong, standalone Envoy veya cloud load balancer), library düzeyinde retry/timeout mantığı ve disiplinli observability. 12 servislik bir deploy’a Istio eklemek, yönetilebilir bir mimariyi yönetilemez hale getirmenin yoludur.

İstisna: servisler arası mTLS’in zorunlu olduğu regüle edilmiş ortamlar. O durumda bile Istio’ya uzanmadan önce Linkerd gibi daha basit bir mesh işi görüyor mu diye bakın.

Modüler Monolit Çoğu Ekip İçin Doğru Cevap

Bu, kimsenin konferans slaytına koymadığı mimari çünkü heyecan verici değil. Aynı zamanda çoğu müşteriye önerdiğimiz şey.

Modüler monolit; bounded context’ler etrafında organize edilmiş, içsel sınırları build sistemi veya konvansiyonla zorlanan tek bir deploy edilebilir artefakt. Modüller birbirlerinin tablolarına uzanarak değil, açık arayüzler üzerinden konuşur. Veritabanı paylaşılır ama schema’lar modül başına sahiplenilir.

src/
  modules/
    billing/        # billing.* tablolarına sahip, BillingAPI sunar
    inventory/      # inventory.* tablolarına sahip, InventoryAPI sunar
    shipping/       # shipping.* tablolarına sahip, ShippingAPI sunar
  shared/
    auth/
    events/         # in-process event bus

Kritik nokta: in-process event bus, Kafka’nın yerini alır. Direkt çağrılar HTTP’nin yerini alır. ACID transaction’lar hâlâ çalışır. Bir modül gerçekten çıkarılmaya ihtiyaç duyduğunda — farklı ölçek, farklı ekip, farklı release temposu — kesilecek temiz bir dikiş hattınız vardır.

Shopify bu şekilde olağanüstü ölçekte çalışıyor. GitHub da öyle. Onlardan büyük olma ihtimaliniz neredeyse sıfır.

Modüler monolit içindeki veri katmanı endişeleri için veritabanı optimizasyonu rehberimiz schema izolasyonunu, connection pooling’i ve read replica’ları kapsıyor — algılanan “veritabanı ölçeklenmiyor” problemlerinin çoğu servis bölmeye başvurmadan burada çözülür.

Database-Per-Service: Muhtemelen İstemediğiniz Takas

Ders kitabı her mikroservisin kendi veritabanına sahip olduğunu söyler. Ders kitabı prensipte haklı, pratikte acımasız.

Kaybettikleriniz: business entity’ler arası join’ler, referential integrity, tek snapshot raporlama, ucuz analitik sorgular. Kazandıklarınız: bağımsız schema evrimi, blast radius izolasyonu, servis başına doğru storage seçme özgürlüğü (billing için Postgres, session için DynamoDB, event için ClickHouse).

Pratikte çoğu ekip hibrit benimser: bir servis kendi write path’ine sahiptir, ama read modelleri CDC (Debezium, Fivetran) ile paylaşılan bir analitik store’a materyalize edilir. Bu pragmatik uzlaşma. Paylaşılan read store olmadan saf database-per-service Netflix için ve neredeyse başka kimse için çalışmıyor.

DevOps Yükü On Kat Artıyor

Bir monolitin ihtiyacı: tek bir CI pipeline, tek deploy hedefi, tek secret seti, tek log akışı, tek dashboard. Mikroservislerin ihtiyacı: bunların her birinin servis başına versiyonu, artı orkestrasyon, service discovery, mesh veya gateway config, distributed tracing, servis başına alerting, dependency graph’lar ve bir release koordinasyon stratejisi.

DevOps pratiğiniz Heroku’da iki kişiyse, hazır değilsiniz. Tek bir servisi bölmeden önce DevOps en iyi uygulamalar rehberimizi ve cloud migration rehberimizi okuyun. Önce o temelleri doğru kurun.

Güvenlik yüzey alanı da katlanır. Her dahili API potansiyel bir saldırı yolu. Servisler arası auth, secret rotation ve network policy enforcement tam zamanlı kaygılar haline gelir. Güvenli web uygulamaları yazımız sadece edge’de değil, her servis sınırında geçerli.

“Mikroservisler mühendislik organizasyonunuzu ölçekler” cümlesinin dürüst versiyonu şu: “Mikroservisler, CI’ı, observability’i, on-call’u ve platform tooling’i zaten çözmüşseniz mühendislik organizasyonunuzu ölçekler. Aksi halde incident’larınızı ölçekler.”

Vaktinizi Gerçekten Hak Eden Okumalar

Martin Fowler’ın 2014 tarihli orijinal mikroservis makalesi hâlâ ayakta — özellikle çoğu ekibin atladığı ön koşullar bölümü. Sam Newman’ın Building Microservices (2. baskı, O’Reilly) çalışma referansı; monolit bölme bölümü konunun en yararlı 40 sayfası.

Okumamanız gereken şey: 100 mühendisten küçük bir şirketin yazdığı “Mikroservislere Nasıl Geçtik” başlıklı herhangi bir blog yazısı. Survivorship bias işin çoğunu yapıyor.

Mimari İncelemeye İhtiyacınız mı Var?

Hızla büyüyen bir monolite bakıp bölme zamanı geldi mi diye düşünüyorsanız — ya da üç yıldır bir mikroservis migrasyonunun ortasındaysanız ve vaat edilen hız kazanımları hiç gelmediyse — bizimle konuşun. Somut bir karar dokümanı üreten ücretli mimari incelemeler yapıyoruz, 200 slaytlık deck değil. İletişime geçin, bir görüşme planlayalım.

Hedef hiçbir zaman “daha çok servis” değil. Hedef daha az incident’la daha hızlı ship etmek. Bazen bu bölmek demek. Sektörün kabul edeceğinden çok daha sık, mevcut modülleri olduğu yerde tutup düzeltmek demek.

Tüm yazılar
Paylaş
IWWOMI

Bir sonraki projeniz için konuşalım

Bu yazıdaki konularda ekibinizin yardıma ihtiyacı varsa, IWWOMI bir mesaj uzakta.

İletişime geç