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

Search for a command to run...

No comments yet. Be the first to comment.
Giriş: Neden Result Pattern'e İhtiyacımız Var? Yazılım geliştirmede hata yönetimi kritik bir konudur. Geleneksel olarak, özellikle C# gibi dillerde, hata durumlarını belirtmek için istisnalar (exceptions) kullanılır. Ancak istisnalar, beklenmedik ve ...
Uygulamalarımızın can damarı olan yapılandırma (configuration) ayarlarını nasıl yönetiyorsunuz? Veritabanı bağlantıları, API anahtarları, servis adresleri... Bu değerli bilgileri kodun içine gömmek (hard-coding) kabul edilemez. Peki bu ayarları yönet...
Go’da html/template ile sunucu tarafı HTML üretimini daha düzenli ve güvenli hale getiriyoruz. Layout sistemiyle tekrar eden yapıları azaltıyor, FuncMap ile şablonlara küçük ama etkili yardımcılar ekliyoruz. Arayüzü de TailwindCSS ile sade ve şık tut...
Mikroservis mimarileri, günümüz yazılım dünyasının parlayan yıldızı. Esneklik, ölçeklenebilirlik ve bağımsız geliştirme imkanı sunarken, karmaşık iş mantığını yönetmek ve sürdürülebilir bir kod tabanı oluşturmak çoğu zaman büyük bir meydan okuma hali...

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 (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, .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
Projeyi oluştur:
dotnet new webapi -n CqrsMediatRDemo
cd CqrsMediatRDemo
dotnet add package MediatR
/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()));
🔹 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;
}
🔹 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);
}
}
🔹 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;
Hayır. Küçük, tek modüllü projelerde fazla gelir. Asıl gücünü karmaşık yapılarda gösterir.
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.
Evet. Command ve Query handler’ları ayrı ayrı test edilebilir, mock dependency yapısı sayesinde unit test'e çok uygundur.
Genellikle ihmal edilebilir düzeydedir. Ancak MediatR gibi katmanlar mikroservislerde değil de, yüksek frekanslı tekil işlemlerde kullanılıyorsa dikkatli olunmalı.
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.