Clean Architecture-1

Clean Architecture-1

Harika — bu soruyu sorman, artık mimariyi “kullanmak” değil anlamak istediğini gösteriyor 🔥
Şimdi çok derin ama kalıcı bir şekilde açıklayacağım:
Clean Architecture, katmanların rolleri, bağımlılık yönü, Core’un görevi, Infrastructure’ın yeri, ve modüler monolith ile ilişkisini.
Hazırsan kahveni al ☕ ve başlıyoruz 👇


🧭 1️⃣ Clean Architecture Nedir?

Clean Architecture, bağımlılık yönünü tersine çevirerek iş kurallarını merkezde tutan bir yazılım mimarisidir.

Amaç:

“Uygulamanın kalbinde iş kuralları yaşar.
Framework’ler, veri tabanı, UI ve üçüncü parti servisler sadece ayrılabilir detaylardır.”

🔹 Katmanlar (içten dışa)

        +----------------------------+
        |        Presentation        |  → UI, API, Web, Console
        +----------------------------+
        |       Infrastructure       |  → DB, Redis, HTTP, FileSystem, Telegram
        +----------------------------+
        |       Application          |  → Use Cases, Orchestration
        +----------------------------+
        |          Domain            |  → Entities, ValueObjects, Rules
        +----------------------------+

🧩 2️⃣ Katmanların Anlamı

1. Domain (Enterprise Rules)

  • Saf iş kuralları ve model (business logic).
  • Persistence, logging, HTTP bilmez.
  • Framework bağımsız.
  • En stabil katman.

İçerik:

  • Entities
  • ValueObjects
  • DomainEvents
  • Aggregates
  • Specifications
  • Interfaces (IDomainEventHandler)

Örnek:

public class Quiz : Entity
{
    public string Title { get; private set; }
    public void Start()
    {
        if (IsStarted) throw new InvalidOperationException("Quiz already started.");
        IsStarted = true;
        AddDomainEvent(new QuizStartedEvent(Id));
    }
}

🟢 Bağımlılığı yok.
Sadece .NET standard kütüphanesine bağlı olabilir.


2. Application (Use Cases + Orchestration)

  • “Domain”i kullanarak iş akışını (use case) oluşturur.
  • Dış sistemlere erişmek için sadece abstractions kullanır (örneğin IQuizRepository, IEventBus, IBufferedLogger).
  • Domain kurallarını uygular ama dış dünyayı bilmez.
  • CQRS, Validation, Mediator gibi pattern’ler burada yaşar.

İçerik:

  • Commands / Queries
  • Handlers
  • DTOs
  • Behaviors (Pipelines)
  • Interfaces (IRepository, IEventBus, IScheduler)

Örnek:

public class StartQuizCommandHandler : IRequestHandler<StartQuizCommand>
{
    private readonly IQuizRepository _repository;
    private readonly IEventBus _eventBus;

    public StartQuizCommandHandler(IQuizRepository repository, IEventBus eventBus)
    {
        _repository = repository;
        _eventBus = eventBus;
    }

    public async Task Handle(StartQuizCommand request, CancellationToken ct)
    {
        var quiz = await _repository.GetByIdAsync(request.QuizId, ct);
        quiz.Start();
        await _repository.SaveAsync(quiz, ct);
        await _eventBus.PublishAsync(new QuizStartedEvent(quiz.Id));
    }
}

🟢 Application katmanı sadece abstractions’a (interface) bağımlı olmalı.
🛑 Infrastructure’a direkt bağımlı olamaz!


3. Infrastructure (Frameworks + Implementation)

  • Dış dünya ile tüm etkileşim burada.
  • Veri tabanı, Redis, API çağrıları, dosya sistemi, e-posta, logging, telemetry…
  • Bu katman, Application katmanında tanımlı interface’leri uygular.

İçerik:

  • EFCoreRepositories
  • RedisCacheService
  • TelegramApiClient
  • BufferedLoggerService
  • OutboxProcessor
  • FileStorageService

Örnek:

public class QuizRepository : IQuizRepository
{
    private readonly AppDbContext _context;
    public QuizRepository(AppDbContext context) => _context = context;

    public Task<Quiz?> GetByIdAsync(Guid id, CancellationToken ct)
        => _context.Quizzes.FirstOrDefaultAsync(q => q.Id == id, ct);

    public Task SaveAsync(Quiz quiz, CancellationToken ct)
        => _context.SaveChangesAsync(ct);
}

🟡 Bu katman dış dünyaya bağımlıdır (EF, Redis, Kafka vs.).
🟢 Ancak bağımlılıklar Application → Interface, Infrastructure → Implementation yönündedir.
🛑 Infrastructure hiçbir zaman Domain veya Application’dan bağımsız olamaz, ama tersine bağımlıdır.


4. Presentation (Interface Adapters / UI Layer)

  • Kullanıcıdan (veya başka sistemden) gelen girdiyle Application katmanını çağırır.
  • ASP.NET Core Controllers, gRPC Services, CLI, SignalR, WebUI olabilir.
  • Request → DTO → Command → Application → Domain → Response akışı.

Örnek:

[ApiController]
[Route("api/quizzes")]
public class QuizController : ControllerBase
{
    private readonly IMediator _mediator;
    public QuizController(IMediator mediator) => _mediator = mediator;

    [HttpPost("{quizId}/start")]
    public async Task<IActionResult> Start(Guid quizId)
    {
        await _mediator.Send(new StartQuizCommand(quizId));
        return Ok();
    }
}

🟢 Presentation sadece Application’a bağımlı olabilir.
🛑 Application veya Domain Presentation’a asla bağımlı olamaz.


🧭 3️⃣ Core Katmanı Nedir?

Core genellikle Domain + Application katmanlarının birleşimidir.

Core/
├── Domain/
│   ├── Entities/
│   ├── ValueObjects/
│   └── Events/
└── Application/
    ├── Interfaces/
    ├── Commands/
    ├── Queries/
    └── Services/

Core = İşin kalbi.
Yani:

  • Uygulamanın iş mantığı burada.
  • Framework bağımlılığı yok.
  • En stabil kısım.

💡 Bu yüzden:

Core içinde Infrastructure olmamalı,
çünkü Infrastructure dış dünyaya bağımlı, değişken bir detaydır.


🧩 4️⃣ Neden Core içinde Infrastructure olmamalı?

🔴 Sebep 1: Dependency Rule (Bağımlılık Yönü)

“Kod bağımlılığı hep içeriye (Domain’e) doğru olmalı.”

Yanlış yön:

Core → Infrastructure ❌

Doğru yön:

Infrastructure → Core ✅

Eğer Core içinde Infrastructure varsa:

  • Domain veya Application, EF, Redis, Telegram gibi framework’lere bağımlı hale gelir.
  • Test edilebilirlik düşer.
  • Değişim maliyeti artar.
  • SOLID prensipleri (özellikle DIP) bozulur.

🔴 Sebep 2: Değişim Sıklığı

  • Infrastructure (örneğin Redis, Kafka, EF) sık değişir.
  • Core (iş mantığı) seyrek değişir.
  • Farklı değişim frekansına sahip kodlar aynı yerde olursa, stabilite bozulur.

🔴 Sebep 3: Test Edilebilirlik

Core içinde Infrastructure olursa:

  • Unit test’ler EF veya Redis’e bağımlı hale gelir.
  • Mock edilemez hale gelir.
  • Pure business logic testleri zorlaşır.

🔴 Sebep 4: Portability (Taşınabilirlik)

  • Core bağımsız olmalı ki aynı iş mantığını başka ortamlara taşıyabilesin.
  • Örneğin:
    Web API, Worker Service, Azure Function, CLI, Mobile App hepsi aynı Core’u kullanabilir.

🧩 5️⃣ Modüler Monolith ve Core’un Rolü

Modüler Monolith = Tek deployable uygulama, ama bağımsız modüller halinde organize edilmiş.

Her modül, küçük bir “Clean Architecture”’tır.

Örnek:

src/
├── Modules/
│   ├── Quiz/
│   │   ├── Quiz.Domain/
│   │   ├── Quiz.Application/
│   │   ├── Quiz.Infrastructure/
│   │   └── Quiz.API/
│   ├── User/
│   │   ├── User.Domain/
│   │   ├── User.Application/
│   │   ├── User.Infrastructure/
│   │   └── User.API/
│   └── Shared/
│       └── SharedKernel/
└── Host/
    └── WebHost/

Avantaj:

  • Her modül kendi mini-Core’una sahip olur (Domain + Application).
  • Tüm modüller Infrastructure katmanını paylaşabilir.
  • Tek database ama bağımsız bounded context’ler.

⚙️ 6️⃣ Bağımlılıklar Nasıl Yönetilir?

📌 Proje Referans Yönü

Presentation  →  Application  →  Domain
      ↓               ↑
 Infrastructure  ←────┘
KatmanReferans verebilirReferans veremez
DomainHiçbirineHepsine ❌
ApplicationDomainInfrastructure ❌
InfrastructureApplication, DomainPresentation ❌
PresentationApplicationInfrastructure ❌

📚 7️⃣ Katmanların İsmi Ne Anlama Geliyor?

KatmanAnlamıTemel Sorumluluk
Domain“Ne iş yapıyorum?”İş kuralları, entity davranışları
Application“Ne zaman / nasıl yapıyorum?”İş akışı, orchestration, use-case’ler
Infrastructure“Nerede / hangi araçla yapıyorum?”DB, API, Redis, Logging
Presentation“Kiminle konuşuyorum?”Kullanıcı / dış sistem arayüzü

🧠 8️⃣ Infrastructure Nasıl Olmalıdır?

Infrastructure = “Uygulamanın donanım, framework, teknoloji detaylarını” içerir.

Özellikleri:

  • Application’daki interface’leri implemente eder.
  • DI (Dependency Injection) ile Application’a bağlanır.
  • Kolay değiştirilebilir olmalı (örneğin Redis yerine Kafka).
  • Testlerde Mock’lanabilir olmalı.

Alt klasör örneği:

Infrastructure/
├── Persistence/
│   ├── AppDbContext.cs
│   ├── QuizRepository.cs
│   └── UnitOfWork.cs
├── Logging/
│   └── BufferedLoggerService.cs
├── Messaging/
│   └── KafkaEventBus.cs
├── Scheduler/
│   └── RedisScheduler.cs
├── Telegram/
│   └── TelegramApiClient.cs
└── DependencyInjection.cs

🧩 9️⃣ Sonuç

KuralAçıklama
✅ Core sadece Domain + Application içerirCore, işin kalbidir; dış dünyayı bilmez
❌ Core içinde Infrastructure olmazBağımlılık yönü ters olur
✅ Application sadece abstraction tanımlarDış dünya Application’a değil, interface’lere bağlıdır
✅ Infrastructure bu interface’leri uygularDB, API, Redis gibi detaylar buradadır
✅ Presentation sadece Application’a bağlıdırController’lar use-case’leri çağırır
✅ Shared, cross-cutting konular içindirCore’un dışında olabilir
✅ Modüler Monolith = birden fazla küçük Clean ArchitectureHer modül kendi Domain + Application setine sahiptir

admin

Leave a Reply

Your email address will not be published. Required fields are marked *