# 🚀 .NET ile CQRS + MediatR Yapısını Kafaya Takmadan Öğren

CQRS ve MediatR kulağa karmaşık geliyor olabilir. Ama aslında doğru yerden bakarsan, gayet okunabilir ve sade bir yapı sunuyor.

Bu yazıda hiç veritabanına bulaşmadan, **sadece bellekte (in-memory)** bir `ToDo` listesi örneğiyle **CQRS + MediatR** nasıl kullanılır onu göstereceğim.

---

## 🧠 CQRS Nedir?

CQRS (Command Query Responsibility Segregation):  
**Yazma (Command)** işlemleri ile **okuma (Query)** işlemlerini birbirinden ayırır. Bu sayede:

* Kodun sorumlulukları ayrılır
    
* Handler’lar sade olur
    
* Okunabilirlik ve test kolaylığı artar
    

## 🧩 MediatR Nedir?

MediatR, .NET dünyasında kullanılan bir kütüphane. Amacı:

* Controller’ın doğrudan servis çağırmaması
    
* Onun yerine bir Request objesiyle işlem yapılması
    

Yani controller → request → MediatR → handler → işlem

## 🏗️ Proje Yapısı

Projeyi oluştur:

```bash
dotnet new webapi -n CqrsMediatRDemo
cd CqrsMediatRDemo
dotnet add package MediatR
```

## 📁 Klasör Yapısı

```plaintext
/CqrsMediatRDemo
│
├── Controllers/
│   └── ToDoController.cs
│
├── Domain/
│   ├── ToDo.cs
│   └── ToDoStore.cs
│
├── Application/
│   └── Features/
│       └── ToDo/
│           ├── CreateToDoCommand.cs
│           ├── CreateToDoHandler.cs
│           ├── GetAllToDosQuery.cs
│           └── GetAllToDosHandler.cs
```

⚙️ Program.cs ayarları

```csharp
builder.Services.AddMediatR(x => x.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
```

## 🧩 Domain Katmanı

🔹 ToDo.cs

```csharp
namespace CqrsMediatRDemo.Domain;

public class ToDo
{
    public int Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public string? Description { get; set; }
    public DateTime CreatedAt { get; set; }
}
```

🔹 ToDoStore.cs

```csharp
namespace CqrsMediatRDemo.Domain;

public static class ToDoStore
{
    public static List<ToDo> ToDos { get; set; } = new();
    public static int CurrentId = 1;
}
```

## 🛠️ Application Katmanı

🔹 CreateToDoCommand.cs

```csharp
using MediatR;

namespace CqrsMediatRDemo.Application.Features.ToDo;

public record CreateToDoCommand(string Title, string? Description) : IRequest<Unit>;
```

🔹 CreateToDoHandler.cs

```csharp
using MediatR;
using CqrsMediatRDemo.Domain;

namespace CqrsMediatRDemo.Application.Features.ToDo;

public class CreateToDoHandler : IRequestHandler<CreateToDoCommand, Unit>
{
    public Task<Unit> Handle(CreateToDoCommand request, CancellationToken cancellationToken)
    {
        var todo = new Domain.ToDo
        {
            Id = ToDoStore.CurrentId++,
            Title = request.Title,
            Description = request.Description,
            CreatedAt = DateTime.UtcNow
        };

        ToDoStore.ToDos.Add(todo);

        return Task.FromResult(Unit.Value);
    }
}
```

🔹 GetAllToDosQuery.cs

```csharp
using MediatR;
using CqrsMediatRDemo.Domain;

namespace CqrsMediatRDemo.Application.Features.ToDo;

public record GetAllToDosQuery : IRequest<List<Domain.ToDo>>;
```

🔹 GetAllToDosHandler.cs

```csharp
using MediatR;
using CqrsMediatRDemo.Domain;

namespace CqrsMediatRDemo.Application.Features.ToDo;

public class GetAllToDosHandler : IRequestHandler<GetAllToDosQuery, List<Domain.ToDo>>
{
    public Task<List<ToDo>> Handle(GetAllToDosQuery request, CancellationToken cancellationToken)
    {
        return Task.FromResult(ToDoStore.ToDos);
    }
}
```

## 🌐 Controller

🔹 ToDoController.cs

```csharp
using MediatR;
using Microsoft.AspNetCore.Mvc;
using CqrsMediatRDemo.Application.Features.ToDo;

namespace CqrsMediatRDemo.Controllers;

[ApiController]
[Route("api/todos")]
public class ToDoController : ControllerBase
{
    private readonly IMediator _mediator;

    public ToDoController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpPost]
    public async Task<IActionResult> Create(CreateToDoCommand command)
    {
        await _mediator.Send(command);
        return Created();
    }

    [HttpGet]
    public async Task<IActionResult> GetAll()
    {
        var result = await _mediator.Send(new GetAllToDosQuery());
        return Ok(result);
    }
}
```

❓ Unit Nedir? CreateToDoCommand gibi işlemlerden veri dönmeyeceksen, MediatR Unit tipini kullanır. Bu void’un async versiyonu gibi düşünebilirsin.

```csharp
public record CreateToDoCommand : IRequest<Unit>;
```

Handler içinde:

```csharp
return Unit.Value;
```

## ❓ Sık Sorulan Sorular

### 💬 Her projede CQRS + MediatR kullanılmalı mı?

Hayır. Küçük, tek modüllü projelerde fazla gelir. Asıl gücünü karmaşık yapılarda gösterir.

### 💬 Service katmanı yoksa ne oluyor?

MediatR zaten bir "aracı" görevi gördüğü için çoğu zaman Service katmanına gerek kalmaz. Handler’lar doğrudan işi yapar.

### 💬 Test yazması kolay mı?

Evet. Command ve Query handler’ları ayrı ayrı test edilebilir, mock dependency yapısı sayesinde unit test'e çok uygundur.

### 💬 Performans farkı var mı?

Genellikle ihmal edilebilir düzeydedir. Ancak MediatR gibi katmanlar mikroservislerde değil de, yüksek frekanslı tekil işlemlerde kullanılıyorsa dikkatli olunmalı.

---

## 🔚 Kapanış

CQRS + MediatR yapısı, ilk bakışta "fazla katmanlı" gibi görünse de büyüyen projelerde **net sorumluluk ayrımı**, **test kolaylığı** ve **daha az kafa karışıklığı** sağlar.

Ancak:

> ⚠️ Küçük projelerde **gereksiz soyutlama**, ileride seni yavaşlatabilir.  
> Bu yapıyı seçmeden önce **gerçekten ihtiyacın olup olmadığını** sorgula.

Bu örnek, CQRS + MediatR yapısının temellerini öğrenmen için fazlasıyla yeterli.  
Gerisi senin ihtiyacına ve projenin büyüklüğüne göre şekillenir.

> 🎯 Kafaya takmadan, adım adım öğrendik.  
> Şimdi sıra sende: Kodla, kurcala, dene.
