Tek süreç, tek dağıtım
Tüm kod tek bir uygulamada koşar; refactor ucuz, transaction'lar basit, dağıtım tek adımdadır. Sınırlar kod düzeyinde gevşekse büyüdükçe modüller birbirine yapışır — "big ball of mud" tehlikesi tek riskidir.
Yazılım mimarisi, kararı geri almanın pahalı olduğu kararların toplamıdır — modül sınırları, bağımlılık yönü, eşzamanlılık modeli. Doğru sınır, ekibin altı ay sonraki hızını da belirler. Bu sayfa monolit ile mikroservis ikileminden hexagonal yaklaşıma, olay-güdümlü sistemlerden test stratejisine kadar — kararlarınızı sadeleştirmek için hazırlandı.
Mikroservis bir hedef değil, belirli bir ölçeğe ulaşmış ekiplerin ödediği bedeldir. Çoğu ürün için doğru başlangıç tek dağıtım birimidir; modüler bir monolit, çoğu kez mikroservise geçmeden de aynı bağımsızlığı verir. Sıralama olgunlukla ilerler — sınırlar oturmadan dağıtık sisteme geçmek, basit bir sorunu ağ üzerinde dağıtık bir soruna dönüştürür.
Tüm kod tek bir uygulamada koşar; refactor ucuz, transaction'lar basit, dağıtım tek adımdadır. Sınırlar kod düzeyinde gevşekse büyüdükçe modüller birbirine yapışır — "big ball of mud" tehlikesi tek riskidir.
Aynı uygulamada koşar ama modüller iyi tanımlı arayüzlerle ayrılır; çapraz çağrılar yalnızca açık API'lerden geçer. Mikroservisin ekip bağımsızlığını, monolitin operasyonel sadeliği ile birleştirir — çoğu ekip için doğru hedef.
Her servis ayrı süreç, ayrı veritabanı, ayrı dağıtım. Ekipler bağımsız çalışır; ölçek seçici olur. Bedeli yüksektir: dağıtık izleme, observability, sözleşmeler, eventual consistency — hepsi yeni problem olarak gelir.
Hexagonal mimari (ports & adapters) tek bir kural koyar: bağımlılık oku içeri dönüktür. Veritabanı, HTTP, kuyruk, e-posta sağlayıcısı — hepsi adapter'dır; iş kuralları onları tanımayı değil, onların kendine uymasını ister. Bu ters çevirme sayesinde Postgres'i SQLite ile değiştirmek, REST'i gRPC'ye çevirmek ya da bir kuyruğu başkasıyla değiştirmek çekirdeği etkilemez.
Aynı fikir farklı isimlerle de anılır: Clean Architecture, Onion Architecture. Sözcükler değişir, kural aynı kalır — dış katman içe bağlanır, iç katman dışı bilmez.
# çekirdek, arayüzü tanımlar (port)
interface OrderRepository {
find(id): Order
save(order: Order): void
}
# servis, somut teknolojiyi değil arayüzü bilir
class PlaceOrder {
constructor(private orders: OrderRepository) {}
}
# adapter, dış dünyayı arayüze uydurur
class PostgresOrders implements OrderRepository { … }
class InMemoryOrders implements OrderRepository { … }
# bağımlılık yönü
adapter → port → çekirdek ✓ doğru
çekirdek → Postgres'i import eder ✗ sızıntı
Senkron bir HTTP çağrısı çağrılan tarafın o an çalışmasını şart koşar; biri yavaşsa zincir yavaşlar, biri düşerse zincir kırılır. Olay-güdümlü yaklaşımda gönderici "şu oldu" bilgisini yayınlar ve gerisine karışmaz. Dinleyenler işlerini kendi hızında yapar — ama tutarlılık artık eventual'dir.
Üç ayrım önemli: command "yap" der ve bir tarafa gider; event "oldu" der ve herkese duyurulur; query sorudur, durumu değiştirmez. Bu üç kavramı karıştırmak en yaygın mesajlaşma hatasıdır.
Hem veritabanına yazıp hem kuyruğa olay göndermek atomik değildir; biri başarısız olursa sistem tutarsız kalır. Outbox örüntüsü, olayı önce aynı transaction'da bir tabloya yazar; ayrı bir süreç onu kuyruğa taşır.
Servisler arası bir akış (sipariş + ödeme + stok), uzun süreli bir transaction yerine adım adım ilerler; herhangi bir adımda hata olursa daha önceki adımlar telafi edici aksiyonlarla geri alınır.
Çoğu kuyruk "en az bir kere" teslim eder; aynı olay iki kez gelebilir. Tüketicinin mesaj kimliğine göre idempotent davranması — aynı mesajı bir kere işlemiş olması — sistemi tutarlı tutar.
# olay, geçmiş zamandadır — bir komut değil
{
"type": "OrderPlaced",
"id": "7f3c…",
"at": "2026-05-21T22:46:19+03:00",
"data": { orderId: "42", total: 198.00 }
}
# sözleşme kural koyar:
alan eklemek güvenli # geriye uyumlu
alan silmek kırıcı # tüketici varsayar
anlam değişikliği en tehlikeli # sessizce yanlış işler
Hangi katmanda ne kadar test yazdığınız, kodunuzun nasıl tutulduğunu söyler. Çok fazla uçtan uca test yavaş ve kırılgan bir sürüm hattı doğurur; tek başına birim testi ise üretimdeki entegrasyon hatalarını yakalayamaz. Klasik piramidi ile daha yeni test trofesi yaklaşımının vurguları farklıdır — ama amaç aynıdır: güvene en hızlı yoldan ulaşmak.
İkisi de tek bir uygulama olarak dağıtılır. Monolit'te modül sınırı çoğunlukla yalnızca klasör adıdır; çapraz çağrılar serbestçe artar. Modüler monolit'te modüller iyi tanımlı API'ler üzerinden konuşur — büyüdüğünde mikroservise geçmek refactor olur, yeniden yazma değil.
Mikroservis bağımsız dağıtım, ayrı veritabanı ve gevşek bağlantı demektir. Dağıtık monolit servislere bölünmüş ama hâlâ birbirine sıkı bağlı (paylaşılan veritabanı, senkron zincir, ortak sürüm) bir kurulumdur — mikroservisin bedeli ödenmiştir, faydası alınmamıştır.
Aynı fikrin iki adıdır: bağımlılık oku içeri dönüktür; çekirdek altyapıyı bilmez, altyapı çekirdeğe uyar. Hexagonal port/adapter sözcüklerini kullanır, Clean halkalı katmanlardan söz eder. Sözcük farkı dışında pratikleri büyük ölçüde örtüşür.
Command bir niyet bildirir ("siparişi kapat"); tek bir alıcıya gider, başarısız olabilir. Event olmuş bir gerçeği bildirir ("sipariş kapandı"); birden çok dinleyiciye gider, geri alınamaz. İkisinin de adı geçmiş zaman/emir kipi olarak doğru yazılmalı — "OrderClose" değil "CloseOrder" (komut) ya da "OrderClosed" (olay).
Piramit tabanda çok birim, tepede az uçtan uca öngörür — birim testin ucuzluğunu öne çıkarır. Trofe entegrasyon testlerini büyütür: gerçek bağımlılıklarla koşan testler, mock ağırlıklı birimden daha gerçekçi güven verir. Modern web uygulamalarında trofe yaklaşımı yaygınlaştı.
Mimari sınırlar, dağıtık sistemler ve test stratejisi üzerine temel kaynaklar.
Mevcut topolojinizi, modül sınırlarınızı ve test stratejinizi masaya yatıralım — sürdürülebilir hızı destekleyen bir yapıyı birlikte planlayalım.