Giriş
Yazılım projelerinde mimari kararlar yalnızca kod organizasyonunu etkilemez. Deploy süreçlerinden ekip yapısına, hata izolasyonundan ölçekleme maliyetine kadar birçok kritik başlık doğrudan mimari seçimden etkilenir. Özellikle .NET ekosisteminde ASP.NET Core, container teknolojileri ve cloud-native araçlar yaygınlaştıkça microservices çok daha erişilebilir hale geldi.
Ama erişilebilir olması, her proje için doğru olduğu anlamına gelmez. Gerçek dünyada doğru soru “Microservices modern mi?” değil, “Bizim problemimizi gerçekten çözüyor mu?” olmalıdır. Çünkü yanlış mimari seçimi, ekibi iş problemlerinden uzaklaştırıp dağıtık sistem karmaşıklığıyla baş başa bırakabilir.
Günümüzde mimari tartışmaların büyük bir kısmı microservices etrafında dönse de birçok büyük teknoloji şirketi sistemlerine aslında monolith olarak başlamıştır. Amazon, Shopify ve GitHub gibi platformların ilk versiyonları tek uygulama mimarisi üzerine kuruluydu. Sistem büyüdükçe domain sınırları netleşmiş ve bazı alanlar bağımsız servisler haline getirilmiştir. Bu nedenle mimari seçim çoğu zaman “hangi mimari daha modern?” sorusundan çok “hangi mimari şu anki sistem ihtiyaçlarına daha uygun?” sorusuyla değerlendirilmelidir.
Problem
Monolith ve microservices aynı temel probleme farklı yaklaşır: büyüyen bir sistemi sürdürülebilir şekilde geliştirmek. Bir uygulama küçükken tek kod tabanı, tek deploy hattı ve tek veritabanı ile ilerlemek genellikle en hızlı yoldur. Bu yapı ürün geliştirme hızını artırır ve operasyonel yükü düşük tutar.
Fakat sistem büyüdükçe bazı sorunlar görünür hale gelir:
- Her değişiklik tüm uygulamayı etkileyebilir
- Tek bir modülün yüksek trafik alması tüm sistemi birlikte ölçeklemeye zorlayabilir
- Ekip büyüdükçe aynı kod tabanı üzerinde çalışma maliyeti artar
- Ortak veri modeli zamanla coupling üretir ve değişiklikleri yavaşlatır
Bu noktada sistem tasarımında iki farklı yaklaşım ortaya çıkar:
- Monolith yaklaşımı, sistemi tek uygulama içinde tutarak karmaşıklığı düşük tutmayı hedefler.
- Microservices yaklaşımı ise sistemi iş kabiliyetlerine göre bölerek ekiplerin ve servislerin bağımsız çalışmasını sağlamayı amaçlar.
Her iki yaklaşım da aynı probleme cevap verir: büyüyen bir sistemi sürdürülebilir şekilde geliştirmek ve yönetmek.
Microservices bu baskıyı azaltmak için servisleri iş kabiliyetlerine göre ayırır. Monolith ise başlangıç aşamasında düşük karmaşıklık ve hızlı teslimat ihtiyacını çok daha iyi karşılar.
Kavram Açıklaması
Monolith, uygulamanın tek bir deployable unit olarak çalıştığı mimaridir. Kod içinde katmanlar ve modüller olabilir, ancak üretim ortamında sistem çoğunlukla tek parça halinde deploy edilir. Çoğu monolith yapıda ortak bir veritabanı da bulunur.
Microservices mimarisinde ise sistem, belirli iş sorumluluklarına göre ayrılmış bağımsız servislerden oluşur. Her servis kendi yaşam döngüsüne, kendi deployment sürecine ve ideal durumda kendi verisine sahiptir. Microsoft da microservices yaklaşımında asıl değerin küçük servislerden çok bağımsız deploy, gevşek bağlılık ve yüksek iç uyum olduğunu vurgular.
Microservices mimarisinde servisler arası iletişim farklı yöntemlerle gerçekleştirilebilir:
- HTTP / REST API → en yaygın kullanılan yöntem
- gRPC → yüksek performans gerektiren servisler için
- Message broker (RabbitMQ, Kafka, Azure Service Bus) → event-driven mimarilerde
Servisler arasındaki bu iletişim modeli, sistemin dağıtık bir yapıya dönüşmesine neden olur ve bu da beraberinde farklı operasyonel zorluklar getirir.
Burada kritik nokta şudur: microservices sadece kodu parçalara bölmek değildir. Aynı zamanda şu operasyonel ihtiyaçları beraberinde getirir:
- Servisler arası iletişim, örneğin HTTP, gRPC veya message broker kullanımı.
- Logging, tracing, health check ve monitoring altyapısı.
- Servis bazlı CI/CD süreçleri.
- Eventual consistency ve dağıtık veri yönetimi.
- Kimlik doğrulama, yetkilendirme ve güvenli servis iletişimi.

Modular Monolith
Monolith çoğu zaman yanlış anlaşılır. Tek uygulama olması, tüm kodun tek bir yerde karışık şekilde bulunması gerektiği anlamına gelmez. İyi tasarlanmış bir monolith mimarisi genellikle modüler bir yapı içerir.
Bu yaklaşım genellikle Modular Monolith olarak adlandırılır.
Bu yaklaşım özellikle son yıllarda birçok ekip tarafından microservices’e geçmeden önceki ara mimari adım olarak tercih edilmektedir.
Bu mimaride:
- Domain modülleri birbirinden ayrılır
- Her modül kendi business logic’ini içerir
- Modüller arası iletişim kontrollüdür
- İç bağımlılıklar minimize edilir
Örnek bir proje yapısı şu şekilde olabilir:
/Modules
/Orders
/Payments
/Catalog
/Users
Bu yapı sayesinde uygulama tek deployable unit olarak kalırken domain sınırları da korunmuş olur. Sistem büyüdüğünde bazı modüllerin microservice olarak ayrılması da daha kolay hale gelir.
Avantajlar
Aşağıdaki tablo WordPress içinde Table bloğu ile rahatça kullanılabilir. Kısa hücreler ve net ifadeler, mobil görünümde okunabilirliği artırır.
| Kriter | Monolith | Microservices |
|---|---|---|
| Başlangıç hızı | Genelde daha hızlıdır | İlk kurulum daha yavaştır |
| Debug kolaylığı | Aynı process içinde daha kolaydır | Distributed tracing gerekir |
| Ölçekleme | Uygulama birlikte ölçeklenir | Servis bazlı ölçekleme mümkündür |
| Deploy modeli | Tek parça deploy edilir | Bağımsız deploy yapılabilir |
| Veri tutarlılığı | Güçlü transaction yönetimi daha kolaydır | Eventual consistency gerekebilir |
| Operasyonel yük | Daha düşüktür | Daha yüksektir |
| Ekip yapısı | Küçük ve orta ekipler için uygundur | Büyük ve otonom ekipler için uygundur |
Monolith’in avantajları
- MVP geliştirmek için hızlıdır.
- Tek repo ve tek deploy süreci yönetimi kolaylaştırır.
- Tek veritabanı ile transaction yönetimi sade kalır.
- Küçük ve orta ekiplerde daha verimli olabilir.
Microservices’in avantajları
- Bağımsız deploy ile release esnekliği sağlar.
- Sadece ihtiyaç duyan servisleri ölçeklemek mümkündür.
- Domain bazlı sahiplik ekip bağımsızlığını artırır.
- Arıza izolasyonu daha kontrollü olabilir.
Dezavantajlar
Monolith’in asıl problemi başlangıçta değil, büyüdükçe ortaya çıkar. Kod tabanı şiştikçe modüller birbirine bağımlı hale gelir, küçük değişiklikler bile tüm sistemi etkileyebilir ve deploy riski artar.
Microservices’in maliyeti ise çok daha erken başlar. Servis keşfi, retry politikaları, güvenli iletişim, distributed logging, trace correlation ve contract yönetimi gibi başlıklar ilk günden itibaren gündeme gelir. Martin Fowler da bu ek maliyeti “microservice premium” olarak tanımlar ve çoğu sistemin önce monolith olarak başlamasının daha güvenli olduğunu söyler.
Özellikle veri yönetimi en zorlu alanlardan biridir. Tek veritabanı yerine servis bazlı veri sahipliği benimsendiğinde, önce kolay olan transaction senaryoları dağıtık hale gelir. Sipariş, ödeme ve stok gibi alanlarda bu durum doğrudan production incident kaynağı olabilir.

Microservices mimarisinde en büyük zorluklardan biri observability konusudur. Tek uygulama yerine birçok servis çalıştığı için hata tespiti ve sistem davranışını anlamak zorlaşır.
Özellikle bir isteğin birden fazla servis üzerinden geçtiği durumlarda request tracing olmadan hatanın kaynağını bulmak oldukça zorlaşır.
Bu nedenle production sistemlerde genellikle şu araçlar kullanılır:
- Centralized logging (ELK, Seq vb.)
- Distributed tracing (OpenTelemetry, Jaeger)
- Metrics monitoring (Prometheus, Grafana)
Bu araçlar olmadan büyük microservices sistemlerini yönetmek oldukça zor hale gelir.
Gerçek Proje Perspektifi
Production ortamında mimari seçim çoğu zaman teorik doğrulukla değil, ekibin olgunluğu ve iş hedefleriyle belirlenir. 5–6 kişilik bir ekibin geliştirdiği, orta trafikli bir B2B yönetim paneli için microservices ile başlamak çoğu zaman gereksiz karmaşıklık yaratır. Böyle bir durumda modüler bir monolith çok daha verimli olabilir.
Buna karşılık e-ticaret, fintech veya lojistik gibi alanlarda durum değişir. Katalog, sipariş, ödeme, kullanıcı yönetimi ve bildirim gibi domain’ler farklı trafik, güvenlik ve release ihtiyaçlarına sahipse microservices gerçek fayda üretmeye başlar. Microsoft’un .NET microservices rehberi de iş kabiliyeti bazlı ayrışmayı temel ilke olarak sunar.
Microservices mimarisinde servislerin nasıl ayrılacağı genellikle domain boundaries ile belirlenir. Domain Driven Design yaklaşımında bu sınırlar bounded context olarak adlandırılır.
Örneğin bir e-ticaret sisteminde şu domain’ler ayrı bounded context olabilir:
- Catalog
- Order Management
- Payment
- Inventory
- User Management
Bu domain’lerin trafik, güvenlik ve veri gereksinimleri farklıysa microservices yaklaşımı gerçek bir fayda üretmeye başlar.
Pratikte en mantıklı yaklaşım çoğu ekip için şudur: önce modüler monolith kur, sonra gerçekten baskı oluşturan domain’leri servisleştir. Bu yaklaşım hem delivery hızını korur hem de erken aşamada gereksiz dağıtık sistem maliyetinden kaçınmayı sağlar.
Ne Zaman Kullanılmalı
Monolith ne zaman mantıklı?
- Ürün henüz erken aşamadaysa.
- Product-market fit net değilse.
- Ekip küçük veya orta ölçekliyse.
- Domain sınırları henüz oturmadıysa.
- Güçlü transaction ihtiyacı baskınsa.
- DevOps ve platform olgunluğu sınırlıysa.
Microservices ne zaman mantıklı?
- Domain karmaşıklığı belirginse.
- Bounded context’ler netleştiyse.
- Farklı modüller farklı ölçekleme ihtiyacı taşıyorsa.
- Ekipler bağımsız release yapacak kadar ayrıştıysa.
- Monitoring, CI/CD ve güvenlik süreçleri oturduysa.
Ne Zaman Kullanılmamalı
Microservices, sırf modern göründüğü için tercih edilmemeli. Aşağıdaki durumlar tipik overengineering örnekleridir:
- Düşük trafikli tek bir backoffice uygulamasını gereksiz sayıda servise bölmek.
- Bütün servislerin aynı veritabanını paylaşması.
- Ekipte gözlemlenebilirlik ve DevOps yetkinliği yokken dağıtık yapıya geçmek.
- Domain sınırları belli olmadan teknik trend nedeniyle servis ayrıştırmak.
Kötü tasarlanmış bir microservices yapısı çoğu zaman dağıtık monolith üretir. Bu durumda hem monolith’in sadeliğini kaybedersiniz hem de microservices’in bağımsızlık faydasını tam elde edemezsiniz.
Aşağıdaki örnek ASP.NET Core minimal API kullanarak basit bir sipariş servisinin nasıl yapılandırılabileceğini gösterir. Bu yapı tek bir monolith uygulama içinde yer alsa bile servis bazlı sorumluluk ayrımının nasıl yapılabileceğini gösterir.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IOrderService, OrderService>();
var app = builder.Build();
app.MapPost("/orders", async (CreateOrderRequest request, IOrderService service) =>
{
var orderId = await service.CreateAsync(request.CustomerId, request.Items);
return Results.Created($"/orders/{orderId}", new { OrderId = orderId });
});
app.Run();
public record CreateOrderRequest(Guid CustomerId, List<OrderItemDto> Items);
public record OrderItemDto(Guid ProductId, int Quantity);
public interface IOrderService
{
Task<Guid> CreateAsync(Guid customerId, List<OrderItemDto> items);
}
public class OrderService : IOrderService
{
public Task<Guid> CreateAsync(Guid customerId, List<OrderItemDto> items)
{
if (items is null || items.Count == 0)
throw new ArgumentException("Sipariş kalemi boş olamaz.");
return Task.FromResult(Guid.NewGuid());
}
}
Kodun mesajı basit: önce modülleri doğru ayır, sonra gerçekten ihtiyaç varsa servisleştir. Birçok ekip için sağlıklı mimari evrim bu şekilde ilerler.
Sonuç
Monolith ve microservices arasında seçim yaparken tek doğru cevap yoktur. Doğru karar; ürün aşamasına, ekip yapısına, domain karmaşıklığına ve operasyonel olgunluğa göre değişir.
Çoğu .NET ekibi için en mantıklı başlangıç noktası iyi tasarlanmış modüler bir monolith’tir. Sistem büyüyüp domain sınırları netleştiğinde, gerçekten baskı oluşturan alanları microservices’e ayırmak çoğu zaman daha güvenli ve daha sürdürülebilir bir stratejidir.
Sonuç olarak microservices bir yazılım trendi değil, çoğu zaman organizasyonel ölçekleme stratejisidir. Bu nedenle mimari kararların teknoloji trendlerinden çok sistemin gerçek ihtiyaçlarına göre verilmesi en sağlıklı yaklaşımdır. Küçük ve orta ölçekli ekipler için iyi tasarlanmış bir monolith mimarisi yıllarca sorunsuz şekilde çalışabilir. Sistem büyüdüğünde ise doğru domain’leri microservices olarak ayırmak çok daha sağlıklı bir evrim sağlar.