Alışveriş Sepeti

Sepetiniz boş

Mimari & Mühendislik · 16 dk

NestJS Modüler Monolith: Neden Erken Mikroservis Çılgınlığına Kapılmadık?

Dağıtık transaction bataklığı, gRPC ağ gecikmeleri ve Kubernetes operasyonel kabusu yerine; NestJS modüler monolith mimarisi ile nasıl milyonlarca liralık ciro ve sıfır gecikme elde ettik?

Yazar: Stilgen Core Engineering

NestJS Modüler Monolith: Neden Erken Mikroservis Çılgınlığına Kapılmadık?

1. Mikroservis Rüyasının Kabusa Dönüştüğü An

Son yıllarda yazılım sektöründe en çok tekrarlanan hatalardan biri, henüz ayda 100 milyon istek görmemiş ekiplerin bile her fonksiyon için (Auth, Ürün, Sepet, Ödeme, Bildirim) ayrı birer mikroservis yazmaya çalışmasıdır.

Bu yaklaşım e-ticarette şu felaketlere yol açar:

  • Dağıtık Transaction (Distributed Transactions / 2PC) Çıkmazı: Sepet onaylandığında Stok servisi düşer, Ödeme servisi parayı çeker, Fatura servisi çökerse ne olacak? Parayı iade etmek için karmaşık Saga Pattern ve kompanzasyon akışları yazmak zorunda kalırsınız.
  • Ağ Gecikmesi (Network Latency Tax): Eskiden tek bir bellek içi çağrı (In-memory function call <0.01ms) olan işlem; HTTP, JSON serialize/deserialize veya gRPC üzerinden 30-50ms ağ gecikmesi ekler. 5 servisi dolaşan bir checkout işlemi müşteriye 300ms gecikme olarak yansır.
  • DevOps ve Altyapı Maliyeti: 12 ayrı servis için 12 CI/CD pipeline'ı, distributed tracing (Jaeger/Zipkin), karmaşık API Gateway ve Kubernetes kümesi yönetmek için tam zamanlı DevOps ordusuna ihtiyaç duyulur.

2. Stilmerce Çözümü: NestJS Modüler Monolith (Modular Monolith)

Modüler Monolith, spagetti monolit ile parçalanmış mikroservislerin tam ortasındaki mükemmel mühendislik dengesidir:

+-------------------------------------------------------------------------+
|                  STILMERCE MODÜLER MONOLITH MİMARİSİ                    |
|                                                                         |
|  [HTTP / GraphQL / WebSocket Giriş Noktası - NestJS API Gateway]        |
|                                │                                        |
|         ┌──────────────────────┼──────────────────────┐                 |
|         ▼                      ▼                      ▼                 |
|  [Auth Module]          [Catalog Module]       [Orders Module]          |
|  - Domain Entities      - Domain Entities      - Domain Entities        |
|  - In-Memory Service    - In-Memory Service    - In-Memory Service      |
|  - Private Repos        - Private Repos        - Private Repos          |
|         │                      │                      │                 |
|         └──────────────────────┴──────────────────────┘                 |
|                                │                                        |
|                                ▼                                        |
|               [Bellek İçi Event Bus & Redis Queue]                     |
|                                │                                        |
|                                ▼                                        |
|                  [PostgreSQL Paylaşımlı Veritabanı]                     |
+-------------------------------------------------------------------------+

Her domain modülü (Catalog, Orders, Billing, Auth) kendi sınırları içinde tamamen bağımsız bir servis gibi yaşar. Birbirleriyle doğrudan veritabanı JOIN'i yapmazlar; yalnızca açıkça tanımlanmış Service API'leri veya bellek içi olaylar (In-memory events) üzerinden konuşurlar.

3. Kod Seviyesinde Domain İzolasyonu ve Sınır Koruması

NestJS'in Inversion of Control (IoC) ve Module sistemi sayesinde, hiçbir modül diğer modülün iç entity'sine veya repository'sine doğrudan erişemez:

// catalog.module.ts
@Module({
  imports: [TypeOrmModule.forFeature([ProductEntity, CategoryEntity])],
  providers: [CatalogService, ProductPricingCalculator],
  // YALNIZCA dışarıya açılmak istenen servis export edilir!
  exports: [CatalogService],
})
export class CatalogModule {}

// orders.service.ts
@Injectable()
export class OrdersService {
  constructor(
    // Doğrudan ProductRepository enjekte EDİLEMEZ! (Derleme hatası verir)
    // Sadece CatalogService public API'si kullanılabilir
    private readonly catalogService: CatalogService,
    private readonly eventEmitter: EventEmitter2,
  ) {}

  async checkout(orderDto: CreateOrderDto) {
    // 0.005 ms bellek içi fonksiyon çağrısı (Ağ gecikmesi sıfır)
    const isValid = await this.catalogService.validateItems(orderDto.items);
    // ...
  }
}

Gerektiğinde Tek Hamlede Mikroservise Bölünme Yeteneği

Kod tabanı katı domain sınırlarıyla yazıldığı için, ileride örneğin OrdersModule'ü bağımsız bir Kubernetes servisi olarak ayırmak istediğimizde tek yapmamız gereken bu modülü ayrı bir repository'e taşımaktır. Refactoring maliyeti haftalar değil, saatler sürer.

4. Kıyaslama: Mikroservis vs Modüler Monolith

Ekip üretkenliği ve sistem performansı açısından karşılaştırma:

Kriter Ham Mikroservisler Stilmerce Modüler Monolith
Modüller Arası Çağrı Gecikmesi 15ms - 45ms (Ağ / HTTP / gRPC) <0.01ms (Bellek içi fonksiyon çağrısı)
Transaction Güvenliği Zor (Saga / İki Aşamalı Commit) Basit & Güvenilir (ACID DB Transactions)
Local Development Kurulumu 15 Docker konteyneri, 32GB RAM şart 1 Docker Compose (DB + Redis + API)
Hata Ayıklama (Debugging) Dağıtık loglar arasında iz sürme Tek Breakpoint ile adım adım inceleme
Bakım ve DevOps Maliyeti Çok Yüksek ($$$) Minimum ($)