Monolith'i Doğru Anlamak: Single-Process, Modular ve Distributed Monolith
Monolith kötü kod değil, tek deployment unit'tir; asıl sorun Big Ball of Mud'dır ve o tek process'te de, yirmi service'te de oluşur. Single-process, modular ve distributed monolith farkı, internal ile compiler'a korunan module sınırları, beş service'lik senkron zincirin %99,9 hedefini nasıl kaçırdığı ve Monolith First tartışması Bookstore üzerinden.
Sürüm notu: Bu yazıdaki kodlar C# 14 ve .NET 10 ile derlenip denenmiştir.
Önce Tahmin Et
Bookstore ekibinin haftalık toplantısında biri bir öneriyle geliyor: "Monolith artık eskidi. Checkout'u beş service'e bölelim: Ordering, Inventory, Payment, Shipping, Notification. Her biri ayrı process, aralarında HTTP (Hypertext Transfer Protocol) çağrısı. Her service'in availability'si %99,9 olsun, checkout da %99,9 olur."
Checkout isteği bu beş service'e sırayla ve senkron gidiyor; biri cevap vermezse sipariş tamamlanamıyor. Her service'in tek başına %99,9 ayakta olduğunu varsayalım. Checkout'un availability'si sence kaç olur? Ayda kaç siparişin hata alacağını tahmin et. Bir de şunu düşün: bu beş service'in hepsi aynı veritabanına bağlıysa ve her release'te beşini birlikte deploy etmek gerekiyorsa, elimizde gerçekten microservices mi var, yoksa başka bir şey mi?
Cevabı simülasyonla vereceğiz. Ama önce "monolith" kelimesinin ne anlattığını netleştirmek gerekiyor, çünkü toplantıdaki öneri kelimeyi "kötü kod" anlamında kullanıyor ve bu, kararı baştan yanlış bir yere götürüyor.
Monolith Nedir: Tek Deployment Unit
Mimari bağlamda monolith, kodun ne kadar düzenli olduğunu değil, kaç parça hâlinde deploy edildiğini anlatır. Sam Newman, Monolith to Microservices (O'Reilly, 2019) kitabında monolith'i deployment unit üzerinden tanımlıyor: sistemin bütün işlevi birlikte deploy edilmesi gereken tek bir birimdeyse elinde bir monolith var. Mark Richards ve Neal Ford'un FoSA (Fundamentals of Software Architecture, 2. baskı, O'Reilly, 2025) kitabı da stilleri ilk olarak bu eksende ayırıyor: tek deployment unit'li monolithic stiller ve birden fazla deployment unit'li distributed stiller.
Bu tanımda değer yargısı yok. Tek process'te çalışan bir uygulama çok temiz de olabilir, çok dağınık da. Bookstore'un bugünkü hâli bir monolith: tek ASP.NET Core uygulaması, tek SQL (Structured Query Language) Server veritabanı, haftada bir deploy. Bunu küçük bir denemeyle görelim. Aşağıdaki solution'da Catalog ve Ordering ayrı project'ler, ama hepsi tek bir host'a derleniyor (gerçek Bookstore'da bu host Bookstore.Web; denemede console uygulaması kullandık):
dotnet publish src/Bookstore.Host -c Release -o out
ls out | grep -E "^Bookstore\..*dll$"
Bookstore.Catalog.dll
Bookstore.Host.dll
Bookstore.Ordering.dll
Üç DLL (Dynamic Link Library), tek publish klasörü, tek process. Project'ler kodu düzenler, deployment unit'i değiştirmez. "Projeyi on project'e böldük, artık monolith değiliz" cümlesi bu yüzden yanlış: on project tek process'te çalışıyorsa hâlâ bir monolith'tesin.
Newman'ın ayrımında üç tür öne çıkıyor. Single-process monolith, bütün kodun tek process olarak deploy edildiği klasik hâl. Modular monolith, bunun bir alt türü: yine tek process, ama içeride birbirinden ayrılmış module'ler ve aralarında açık sınırlar var. Distributed monolith ise birden fazla service'ten oluşan ama hepsinin birlikte deploy edilmesi gereken sistem. Kitap ayrıca kaynak koduna sahip olmadığın üçüncü parti black-box sistemleri de bu başlık altında anıyor; onları bu yazının dışında bırakıyoruz.

Big Ball of Mud: Monolith Nasıl Çamura Döner?
Monolith'in kötü ün kazanmasının sebebi aslında başka bir pattern: Big Ball of Mud. Brian Foote ve Joseph Yoder bu adı 1997'de PLoP (Pattern Languages of Programs) konferansında sundukları makalede verdiler. Tarif ettikleri sistem, yapısı plansız büyümüş, düzeni tasarımdan çok o günün aceleciliğini yansıtan, bilginin birbirinden uzak parçalar arasında gelişigüzel paylaşıldığı bir kod tabanı. Makalenin dikkat çekici iddiası, bunun pratikte en yaygın mimari olduğu.
Foote ve Yoder çamuru oluşturan kuvvetleri tek tek sayıyor: zaman baskısı, mimarinin maliyetinin hemen, faydasının geç görünmesi, ekibin domain'i henüz tanımaması, kişiler arası beceri farkı, mimarinin dışarıdan görünmemesi, domain'in kendi karmaşıklığı, öngörülmeyen değişiklikler ve ölçek. Hiçbiri kötü niyet değil. Bookstore'da süreç şöyle işler: kampanya haftası yaklaşıyor, checkout'ta indirimli fiyat hesaplanması gerekiyor ve OrderService'i yazan arkadaş Catalog'un fiyat tablosuna doğrudan bir join atıyor. Bir sonraki ay Inventory raporu Orders tablosunu okuyor, Notification da müşteri e-postasını Customers tablosundan kendisi çekiyor. Her adım kendi içinde makul, toplamda kimin neye bağımlı olduğunu kimse bilmiyor.
Makaledeki diğer pattern'ler çamurla yaşamanın yollarını anlatıyor. Piecemeal Growth sistemin küçük adımlarla büyümesinin kaçınılmaz olduğunu, ama her adımdan sonra toparlamak gerektiğini söylüyor. Shearing Layers, farklı hızda değişen parçaların ayrılmasını öneriyor. Sweeping It Under the Rug, düzeltilemeyen dağınık bir bölgeyi temiz bir interface'in arkasına saklamayı; Design Patterns serisindeki Facade Pattern yazısının anlattığı şey tam olarak bu. Reconstruction ise kurtarılamayan sistemi baştan yazmak.
Buradan çıkan ilk ders: Big Ball of Mud bir deployment kararı değil, bir yapı problemi. Çamur tek process'te de oluşur, yirmi service'e dağılmış bir sistemde de. Çamurlu bir monolith'i microservice'lere bölersen çoğu zaman elinde network üzerinden konuşan bir çamur olur. Distributed monolith'e birazdan bu yüzden geleceğiz.
Technical ve Domain Partitioning
Bir monolith'in iç düzenini anlamanın en hızlı yolu kodun neye göre bölündüğüne bakmak. FoSA bu ayrıma technical ve domain partitioning diyor. Bir önceki yazıdaki Layered Architecture technical partitioning'in tipik örneği: kod presentation, business ve data gibi teknik rollere göre ayrılıyor.
src/
Bookstore.Web/
Controllers/ BooksController, OrdersController, ShipmentsController
Bookstore.Business/
Services/ CatalogService, OrderService, StockService, PaymentService
Bookstore.Data/
Repositories/ BookRepository, OrderRepository, StockRepository
Domain partitioning'de ise üst seviye bölünme iş alanına göre yapılıyor; teknik roller her module'ün içine iniyor:
src/
Bookstore.Catalog/ Book, Author, search, price display
Bookstore.Ordering/ Order, OrderItem, checkout
Bookstore.Inventory/ StockItem, reservation
Bookstore.Payments/ Payment, provider integration
Bookstore.Shipping/ Shipment, tracking
İkisinin farkı en net, gerçek bir değişiklik isteğinde görünüyor. "Sipariş notu ekle" isteği technical partitioning'de üç project'e dokunur: controller'a field, service'e kural, repository'ye kolon. Domain partitioning'de değişiklik tek bir module'ün içinde kalır. Tersine, "bütün veri erişimini Dapper'a taşı" gibi teknik bir değişiklik technical partitioning'de tek project'e, domain partitioning'de her module'e dokunur. Hangi tür değişikliği daha sık yaptığın, hangi bölünmenin sana uyduğunu söyler. Bookstore'da isteklerin büyük çoğunluğu iş alanından geliyor: arama filtresi, kısmi iptal, kargo takip sayfası. Bu, büyüdükçe domain partitioning'e doğru gitmek için güçlü bir işaret.
Partitioning ekip yapısıyla da doğrudan ilgili. Kodu layer'lara bölüp ekipleri de layer'lara göre kurarsan, her özellik üç ekibin koordinasyonunu gerektirir. Bu konuyu sekizinci yazıda Conway's Law ile ölçerek ele alacağız.
Modular Monolith: Sınırı Compiler'a Korut
Modular monolith, domain partitioning'i tek deployment unit içinde ciddiye almak demek. Her module'ün dışarıya açtığı küçük bir public contract var, geri kalan her şey module'ün içinde gizli. Module'ler birbirini yalnız bu contract üzerinden, network olmadan, sıradan bir method çağrısıyla kullanıyor. C#'ta bunun en ucuz aracı internal access modifier'ı:
using Microsoft.Extensions.DependencyInjection;
namespace Bookstore.Catalog;
// Module'ün dışarıya açık contract'ı: yalnız bunlar public
public interface ICatalogApi
{
BookSummary? FindBook(string isbn);
}
public sealed record BookSummary(string Isbn, string Title, decimal Price);
public static class CatalogModuleRegistration
{
public static IServiceCollection AddCatalogModule(this IServiceCollection services) =>
services.AddSingleton<ICatalogApi, CatalogApi>();
}
// Module'ün içi: entity ve implementation internal
internal sealed class Book(string isbn, string title, string author, decimal price)
{
public string Isbn { get; } = isbn;
public string Title { get; } = title;
public string Author { get; } = author;
public decimal Price { get; } = price;
}
internal sealed class CatalogApi : ICatalogApi
{
private readonly Dictionary<string, Book> _books = new()
{
["978-0441013593"] = new("978-0441013593", "Dune", "Frank Herbert", 12.50m),
["978-0141439518"] = new("978-0141439518", "Emma", "Jane Austen", 6.00m)
};
public BookSummary? FindBook(string isbn) =>
_books.TryGetValue(isbn, out Book? book) ? new BookSummary(book.Isbn, book.Title, book.Price) : null;
}
Ordering module'ü Catalog'u yalnız ICatalogApi üzerinden tanıyor:
using Bookstore.Catalog;
using Microsoft.Extensions.DependencyInjection;
namespace Bookstore.Ordering;
public sealed record OrderConfirmation(Guid OrderId, string Title, decimal Total);
public interface IOrderService
{
OrderConfirmation PlaceOrder(string customerId, string isbn, int quantity);
}
public static class OrderingModuleRegistration
{
public static IServiceCollection AddOrderingModule(this IServiceCollection services) =>
services.AddSingleton<IOrderService, OrderService>();
}
// Catalog'a yalnız public contract üzerinden, in-process bir method çağrısıyla gidiyor
internal sealed class OrderService(ICatalogApi catalog) : IOrderService
{
public OrderConfirmation PlaceOrder(string customerId, string isbn, int quantity)
{
BookSummary book = catalog.FindBook(isbn)
?? throw new InvalidOperationException($"Unknown book {isbn}");
return new OrderConfirmation(Guid.CreateVersion7(), book.Title, book.Price * quantity);
}
}
Host iki module'ü kaydedip bir sipariş veriyor:
using System.Globalization;
using Bookstore.Catalog;
using Bookstore.Ordering;
using Microsoft.Extensions.DependencyInjection;
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
ServiceCollection services = new();
services.AddCatalogModule().AddOrderingModule();
using ServiceProvider provider = services.BuildServiceProvider();
IOrderService orders = provider.GetRequiredService<IOrderService>();
OrderConfirmation confirmation = orders.PlaceOrder("customer-42", "978-0441013593", 2);
Console.WriteLine($"{confirmation.Title} x2 = {confirmation.Total}");
IEnumerable<string?> modules = AppDomain.CurrentDomain.GetAssemblies()
.Select(a => a.GetName().Name)
.Where(name => name!.StartsWith("Bookstore."))
.Order();
Console.WriteLine($"Loaded modules: {string.Join(", ", modules)}");
Dune x2 = 25.00
Loaded modules: Bookstore.Catalog, Bookstore.Host, Bookstore.Ordering
Asıl değer, kuralı çiğnemeye çalıştığında ortaya çıkıyor. Kampanya haftasında birinin "daha hızlı" diye Catalog'un entity'sine doğrudan uzandığını düşün:
using Bookstore.Catalog;
namespace Bookstore.Ordering;
internal static class Shortcut
{
// "Daha hızlı" diye Catalog'un entity'sine doğrudan uzanmak
public static decimal PriceOf(string isbn) => new Book(isbn, "?", "?", 0m).Price;
}
src/Bookstore.Ordering/Shortcut.cs(8,55): error CS0122: 'Book' is inaccessible due to its protection level
Big Ball of Mud'ı başlatan küçük adım burada derlenmiyor. Sınırı code review'daki bir yoruma değil, compiler'a emanet ettin. Bunun bir bedeli de var: Ordering'in ihtiyaç duyduğu her bilgi için Catalog'un contract'ını genişletmek gerekiyor ve bu, "neyi dışarı açıyorum" sorusunu her seferinde sormak demek. İyi bir module contract'ı küçük ve istikrarlı kalır; SOLID (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion) ve Design Principles serisindeki "Component Prensipleri: .NET Solution'ını Project'lere Bölmek" yazısı bu dengenin ölçülerini anlatıyor.
internal tek başına yetmez. InternalsVisibleTo attribute'u bir module'ün içini başka bir assembly'ye açabilir ve bir kez test için açıldığında başka amaçlarla da kullanılmaya başlar. Daha önemlisi veri: Ordering, Catalog'un tablolarına SQL ile doğrudan erişebiliyorsa C# seviyesindeki sınır kâğıt üzerinde kalır. Module başına schema, module'ler arası integration event'ler ve sınırları test'le korumak ilerideki Modular Monolith serisinin konusu; sınırları otomatik test'le korumanın ilk adımını da bu serinin son yazısında atacağız.
Distributed Monolith: Bedeli Öde, Faydasını Alma
Distributed monolith, birden fazla service'e bölünmüş ama service'leri birbirinden bağımsız deploy edilemeyen sistem. Newman'ın sık tekrarladığı test basit: bir service'i değiştirmek için diğerlerini de aynı anda deploy etmek zorundaysan, microservices değil distributed monolith kurmuşsun. Microservices'in vaat ettiği şey independent deployability; bu olmadan geriye yalnız network'ün bedeli kalıyor.
Belirtiler genelde birlikte görünür:
- Release günü bütün service'ler aynı anda ve belli bir sırayla deploy ediliyor.
- Service'ler aynı veritabanını, hatta aynı tabloları paylaşıyor; bir kolon değişikliği birden fazla service'i kırıyor.
- Domain model'ini taşıyan ortak bir NuGet paketi var ve paket sürümü değişince herkes yeniden deploy ediyor.
- Bir kullanıcı isteği birkaç service'ten senkron geçiyor ve zincirdeki her halka isteğin kaderini belirliyor.
- Bir service'teki hata ya da yavaşlık, diğerlerinde timeout olarak görünüyor.
Açılıştaki soruya dönelim. Checkout'un beş service'ten senkron geçtiği durumu bir milyon istekle simüle ettik. Her hop'un %99,9 başarılı olduğunu, cevap süresinin çoğunlukla 15–25 ms olduğunu, ama %2 ihtimalle 200–300 ms'ye çıktığını varsaydık:
using System.Globalization;
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
// Checkout isteği sırayla 5 service'e senkron gidiyor
string[] chain = ["Ordering", "Inventory", "Payment", "Shipping", "Notification"];
const double hopAvailability = 0.999; // her service tek başına %99.9
const int requests = 1_000_000;
Random random = new(42);
int succeeded = 0;
List<double> latencies = new(requests);
for (int i = 0; i < requests; i++)
{
double total = 0;
bool ok = true;
foreach (string _ in chain)
{
total += Hop(random);
if (random.NextDouble() > hopAvailability) { ok = false; break; }
}
if (ok) { succeeded++; latencies.Add(total); }
}
latencies.Sort();
List<double> single = Enumerable.Range(0, requests).Select(_ => Hop(random)).Order().ToList();
Console.WriteLine($"Expected availability : {Math.Pow(hopAvailability, chain.Length):P2}");
Console.WriteLine($"Observed availability : {(double)succeeded / requests:P2}");
Console.WriteLine($"Failed checkouts : {requests - succeeded:N0} of {requests:N0}");
Console.WriteLine($"Single hop p50/p90/p99 : {P(single, 0.50):F0} / {P(single, 0.90):F0} / {P(single, 0.99):F0} ms");
Console.WriteLine($"Whole chain p50/p90/p99 : {P(latencies, 0.50):F0} / {P(latencies, 0.90):F0} / {P(latencies, 0.99):F0} ms");
// Bir hop: çoğunlukla 15-25 ms, %2 ihtimalle 200-300 ms yavaş cevap
static double Hop(Random r) => r.NextDouble() < 0.02 ? 200 + r.NextDouble() * 100 : 15 + r.NextDouble() * 10;
static double P(List<double> sorted, double q) => sorted[(int)Math.Ceiling(q * sorted.Count) - 1];
Expected availability : 99.50 %
Observed availability : 99.51 %
Failed checkouts : 4,894 of 1,000,000
Single hop p50/p90/p99 : 20 / 24 / 250 ms
Whole chain p50/p90/p99 : 101 / 116 / 374 ms
Senkron bir zincirde availability'ler çarpılır: 0,999 üzeri 5, yaklaşık 0,995. Bookstore'un checkout için koyduğu aylık %99,9 hedefi, her service tek başına bu hedefi tutsa bile kaçırılıyor; bir milyon checkout'ta yaklaşık 4.900 hata, hedefin izin verdiğinin beş katı. Latency tarafında da tablo benzer. Tek bir hop'ta yavaş cevap %2 ihtimalle geliyor, beş hop'luk zincirde ise isteklerin yaklaşık %10'u (1 − 0,98⁵) en az bir yavaş hop'a denk geliyor. Tek service'te p90'ın hiç görmediği yavaşlık zincirde p90'ın hemen eşiğine geldi; p99 da 250 ms'den 374 ms'ye çıktı.
Aynı beş parça bir modular monolith'te in-process method çağrısı olsaydı, bu çarpımların hiçbiri olmazdı: process ya ayaktadır ya değildir. Bu, distributed sistemlerin kötü olduğu anlamına gelmiyor. Network'ün bedelini ancak karşılığında bir şey alıyorsan ödemelisin: bağımsız deploy, bağımsız ölçekleme, hata izolasyonu. Distributed monolith bedeli ödeyip bunların hiçbirini almıyor. Timeout, retry ve circuit breaker gibi araçlar zinciri hafifletir ama sorunun kendisi mimaride; bu araçlar System Design serisinde, independent deployability ve service sınırları Microservices serisinde ayrıntılı ele alınacak.
Monolith First ve Karşı Görüş
Martin Fowler, Haziran 2015'te yazdığı MonolithFirst yazısında iki gözlem paylaşıyor: duyduğu başarılı microservice hikâyelerinin neredeyse hepsi büyüyüp bölünen bir monolith'le başlamış, sıfırdan microservice olarak kurulan sistemlerin neredeyse hepsi ciddi sorun yaşamış. Gerekçesi iki ayaklı. İlki hız ve belirsizlik: ürünün kullanıcıya değer verip vermediğini henüz bilmiyorken microservice'lerin ek yükü seni yavaşlatır. İkincisi sınırlar: service sınırlarını doğru çizmek baştan çok zor, yanlış çizilmiş bir sınırı service'ler arasında taşımak ise monolith içinde taşımaktan çok daha pahalı. Fowler monolith'in sonunda tamamen atılabileceğini de kabul ediyor ve bunu bilinçli bir "sacrificial architecture" olarak görüyor.
Karşı görüşü, altı gün sonra yine martinfowler.com'da yayımlanan "Don't start with a monolith" yazısında Stefan Tilkov dile getiriyor. Tilkov'a göre bir sistemi bölmenin en kolay anı onu sıfırdan tasarladığın an; monolith'in içindeki module sınırları pratikte nadiren korunuyor, ortak domain object'leri, ortak transaction'lar ve ortak persistence modeli zamanla her şeyi birbirine bağlıyor ve sonradan bölmek çoğu zaman neredeyse imkânsız hâle geliyor. Önemli bir şartı da var: bu yaklaşımı domain'i çok iyi tanıyorsan, ideal olarak var olan bir sistemin ikinci sürümünü yazıyorsan öneriyor.
İki yazı birbirini dışlamıyor. Fowler'ın endişesi yanlış sınır, Tilkov'unki korunmayan sınır. İkisini birlikte okuyunca çıkan pratik sonuç şu: sınırları erken düşün, ama ucuz değiştirilebilecekleri yerde, yani tek deployment unit içinde tut ve korunmalarını otomatikleştir. Modular monolith tam olarak bu ara yol.
Bookstore'un ilk hâli bu yüzden bir monolith. Ekip 3 developer ve yarı zamanlı bir ops kişisinden oluşuyor; haftada bir deploy yeterli; domain'in sınırları (kısmi iade Ordering'in mi Payment'ın mı işi?) henüz netleşiyor ve öncelikli characteristic'ler arasında independent deployability bilinçli olarak "şimdilik değil" diye işaretlendi. Bu koşullarda beş service'in getireceği deploy, izleme ve network yükünü taşıyacak kimse yok. Karar geri dönülemez de değil: module sınırları korunursa, bir module'ü sonradan service'e çıkarmak mümkün kalır.
Sık Düşülen Tuzaklar
Monolith'i kötü kodla eş tutmak. Sorun deployment unit sayısı değil, iç yapı. Çamurlu bir monolith'i bölmek çamuru network'e taşır.
Project sayısını modülerlikle karıştırmak. Elli project'lik bir solution'da her şey public ise ve her project diğerine referans veriyorsa, elinde elli dosyaya yayılmış tek bir module var. Modülerliği project sayısı değil, neyin gizli kaldığı belirler.
Veritabanını unutmak. C# tarafında internal ile korunan bir sınır, raporlama sorgusu bütün tabloları join'lediği anda delinir. Module başına schema ya da en azından "bu tabloya kim yazar" listesi olmadan modular monolith yarım kalır.
Shared kernel'i büyütmek. "Ortak" adlı bir project'e önce Money, sonra Customer, sonra OrderStatus girer; bir süre sonra her module ortak project üzerinden birbirine bağlıdır. Ortak kodu küçük ve domain'den bağımsız tut.
Distributed monolith'i microservices sanmak. Service'ler ayrı container'larda çalışıyor diye bağımsız değiller. Ölçüt, bir service'in diğerlerine dokunmadan deploy edilip edilemediği.
Monolith First'ü "module'süz başla" diye okumak. Fowler'ın önerisi sınırsız bir monolith değil; tersine, iyi modüler bir monolith'in sonradan bölmeyi kolaylaştırdığını söylüyor.
Ne Zaman Monolith'ten Çıkmalı?
Monolith'ten çıkmak için gerekçe, monolith'in kendisi değil, onun karşılayamadığı somut bir characteristic olmalı. Gerçek sinyaller şunlar: farklı ekipler birbirlerinin release'ini beklemekten yavaşlıyor ve bu, ölçülebilir bir teslim süresine yansıyor; bir parçanın yükü diğerlerinden çok farklı ve onu ayrı ölçeklemek ciddi maliyet farkı yaratıyor (Bookstore'da kampanya dönemindeki katalog araması gibi); bir parçanın çökmesi bütün sistemi düşürüyor ve bu izolasyon iş açısından gerekli; ya da bir parçanın tamamen farklı bir teknolojiye ihtiyacı var.
Bu sinyaller yoksa, "Netflix de microservices kullanıyor" bir gerekçe değil. Sinyaller varsa bile ilk adım genelde module sınırlarını netleştirmek, sonra tek bir module'ü (çoğunlukla en bağımsız olanı) dışarı çıkarmak. Bu geçişin teknikleri, Strangler Fig ve Branch by Abstraction gibi pattern'ler, Microservices serisinin son yazısında ele alınacak.
Tersi yönde de dikkatli ol: bazı sistemler hiçbir zaman monolith'ten çıkmaz ve bunda bir sorun yok. Tek ekibin bakım yaptığı, yükü tek sunucuya sığan, haftada bir deploy edilen bir uygulama için modular monolith çoğu zaman en az kötü seçenek.
Küçük Bir Deneme
★☆☆ Kendi çalıştığın bir solution'da dotnet publish çıktısına bak. Kaç deployment unit var? Project'lerin kaçında internal anahtar kelimesi hiç geçmiyor? Bu iki sayı sana modülerlik hakkında ne söylüyor?
★★☆ Bu yazıdaki Catalog ve Ordering module'lerine bir Inventory module'ü ekle. OrderService sipariş vermeden önce stok sorsun, ama yalnız Inventory'nin public contract'ını kullansın. Sonra Ordering'den Inventory'nin internal bir type'ına erişmeyi dene ve CS0122 hatasını gör. Hangi bilgiyi contract'a eklemek zorunda kaldın?
★★★ Simülasyonu değiştir: zincirdeki Notification çağrısını senkron zincirden çıkar ve yalnız başarılı checkout'lardan sonra, sonucu beklemeden "fire and forget" olarak çalıştır. Availability ve p99 nasıl değişiyor? Sonra Notification'ın hata oranını %1'e çıkar; checkout etkileniyor mu? Bu değişikliğin bedeli ne (kaybolan e-postalar, retry ihtiyacı)?
Bu yazıda monolith'in üç türünü ve aralarındaki çizgiyi konuştuk. Bir sonraki yazıda haritayı genişletiyoruz: layered ve modular monolith'in yanına pipeline, microkernel, event-driven, space-based, service-based ve microservices geliyor. Her birini hangi constraint'lerin tanımladığını, Bookstore bağlamında hangi characteristic'lerde iyi ya da kötü olduklarını ve hangi seride derinleştiklerini tek bir tabloda toplayacağız.