🚀 .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:
dotnet new webapi -n CqrsMediatRDemo
cd CqrsMediatRDemo
dotnet add package MediatR
📁 Klasör Yapısı
/CqrsMediatRDemo
│
├── Controllers/
│ └── ToDoController.cs
│
├── Domain/
│ ├── ToDo.cs
│ └── ToDoStore.cs
│
├── Application/
│ └── Features/
│ └── ToDo/
│ ├── CreateToDoCommand.cs
│ ├── CreateToDoHandler.cs
│ ├── GetAllToDosQuery.cs
│ └── GetAllToDosHandler.cs
⚙️ Program.cs ayarları
builder.Services.AddMediatR(x => x.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
🧩 Domain Katmanı
🔹 ToDo.cs
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
namespace CqrsMediatRDemo.Domain;
public static class ToDoStore
{
public static List<ToDo> ToDos { get; set; } = new();
public static int CurrentId = 1;
}
🛠️ Application Katmanı
🔹 CreateToDoCommand.cs
using MediatR;
namespace CqrsMediatRDemo.Application.Features.ToDo;
public record CreateToDoCommand(string Title, string? Description) : IRequest<Unit>;
🔹 CreateToDoHandler.cs
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
using MediatR;
using CqrsMediatRDemo.Domain;
namespace CqrsMediatRDemo.Application.Features.ToDo;
public record GetAllToDosQuery : IRequest<List<Domain.ToDo>>;
🔹 GetAllToDosHandler.cs
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
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.
public record CreateToDoCommand : IRequest<Unit>;
Handler içinde:
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.
Subscribe to my newsletter
Read articles from Uygar Öztürk Ceylan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
