Selam Dostlar, uzun bir aradan sonra tekrardan sizlerleyim… Umarım hepiniz iyisinizdir, iş ilanların azaldığı ve sektörün bu denli kötü olduğu bir dönemde umut dileklerim sizlerle... Yazı dizilerime uzun zamandır ara vermiştim hem işlerimin yoğunluğu hemde LLM’lere ayırdığım ciddi zamandan dolayı bu tarafı ihmal ettiğimi fark ettim. Hafta sonu tatilini fırsat bilerek bu güzel makaleyi yazmaya karar kıldım. Diğer yazı dizilerimi okuduysanız eğer kullandığım bir teknik vardır. Sanki sizlerle konuşuyormuşum gibi makalelerimi yazıyorum… Bu kadar detaydan sonra konuya girebiliriz, herhalde, galiba, sanırsam... 😄
Giriş
Konuya çoğu developer’dan duyduğum bir terim karmaşasını düzelterek girmek istiyorum. MediatR, .NET için geliştirilmiş bir Nuget paketidir. Mediator Pattern ise bir tasarım desenidir. GoF ( Gang of Four) tarafından tanımlanmıştır ve MediatR paketi, Mediator Pattern’in bir implementasyonudur. Yani bu tasarım desenini uygulamak için yazılmış bir .NET nuget kütüphanesidir. İkisi aynı şey değildir, bunu netleştirelim önce. 🤗
Mediator Pattern Nedir?
Yazılım geliştirme süreçlerinde bağımlılığı azaltmak ve yeniden kullanabilirliği arttırmak için çeşitli Design Pattern’lar uygulanır.
● Bu desenlerden biri olan Mediator Pattern, özellikle karmaşık nesneler arası iletişimleri merkezi (MedaitR) bir yapı üzerinden düzenlemek için tercih edilir.
● Tasarım desenleri genel olarak üç gruba ayrılır: Creational (Yaratımsal), Structural (Yapısal) ve Behavioral (Davranışsal). Mediator Pattern ise bu gruplardan Behavioral Patterns içerisinde yer alır.
1994 yılında Gang of Four (GoF) olarak bilinen 4 yazar tarafından Design Patterns: Elements of Resuable Object-Oriented Software kitabında geçen 23 tasarım kalıbından biride Mediator Pattern’dır.

Dahada basit bir şekilde işlevini anlatmak gerekirse, Mediator Pattern "Arabulucu Avukat" gibi düşünebilirsiniz. Bir sistemdeki nesnelerin bir biriyle doğrudan konuşmak yerine, tek bir merkezle konuşmasını sağlar.
Gerçek Hayattan Basit Örnek: Arabulucu Avukat
Bir boşanma sürecinde, eşlerin doğrudan birbiriyle konuşması gerginliğe ve kaosa yol açabilir. Bunun yerine, her iki taraf da sadece arabulucu avukat ile konuşur. Arabulucu, taraflar arasında ki iletişimi sağlar, uzlaşı yolları sunar ve süreci sadeleştirir.
Burada:
- Eşler = Sistemdeki farklı nesneler
- Arabulucu Avukat = Mediator (arabulucu desenindeki merkez nesne)
Bu sayede sistemde doğrudan, karmaşık bağlantılar yerine tek bir noktadan yönetilen sade ve kontrol edilebilir bir iletişim modeli oluşur.
[Eş1] [Eş2]
| |
| |
+----+ +----+
| |
[Arabulucu Avukat]
|
---------------------
| Görüşmeleri |
| yöneten tek |
| merkez kişi |
---------------------
MediatR Nedir?
MediatR, .NET için geliştirilmiş, açık kaynak bir nuget paket kütüphanesidir. Eğer ki bir projede CQRS (Command-Query Responsibility Segregation) denk geldiyseniz büyük ihtimal orada MediatR'da kullanılıyordur. Çünkü nesneler arası iletişimi gevşek bağlı (loose coupling) hale getirmek için CQRS gibi desenleri kolayca uygulamanıza yardımcı olur. Github repo linki ve yazarın adını sizlerle paylaşıyorum, merak edenler github repo'sunu inceleyebilirler.
Yazar: Jimmy Bogard
GitHub: https://github.com/jbogard/MediatR
MediatR, arka planda Mediator Pattern'i uygular. Nesnelerin birbirini direkt tanımadan iletişim kurmasını sağlar. Daha kalıcı hale getirmek için basit bir kod scop'u üzerinden örnekleye biliriz kütüphanenin işlevini.
Klasik Yaklaşım:
// Controllers/UsersController.cs
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
[HttpPost]
public IActionResult Create([FromBody] UserDto user)
{
var service = new CreateUserService(); // doğrudan bağımlılık
service.Create(user);
return Ok("User created.");
}
}
UsersController, CreateUserService sınıfını doğrudan bağımlıdır. Test etmesi zor ve değişime kapalıdır.
MediatR Yaklasimi (Loosely Coupled)
// Controllers/UsersController.cs
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IMediator _mediator;
public UsersController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] UserDto user)
{
var command = new CreateUserCommand(user.Name, user.Email);
var result = await _mediator.Send(command);
return Ok(result);
}
}
- Controller artık CreateUserService'i tanımıyor.
- Sadece IMedator'a bağlı.
- Hangi komutu hangi sınıfın işlediği Controller'ın umrunda değildir.
- Unit test yazmak kolaylaştı.
- Yeni iş kuralları eklendiğinde controller değişmez.
- SOLID prensiplerine uygun hale geldi.
MediatR Nasıl Çalışır?
* IRequest<T>: Bir "istek" modelidir.
* IRequestHandler<TRequest, TResponse>: Bu isteği "işleyen" sınıftır.
* IMediator: Tüm istekleri yöneten merkez.
Projenize MediatR nuget paketini yükledikten sonra IRequestHandler interface’ine F12 (go to defination) yaparak class yapısının içini incelemenizi tavsiye ederim. 😃
Model tanımından sonra kullanımını örnekleye biliriz:
1) Command Oluşturuyoruz
public record CreateUserCommand(string Name, string Email) : IRequest<string>;
2) Handle’i Yazıyoruz:
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, string>
{
public async Task<string> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
// Burada veritabanına kaydetme işlemi yapılır
return $"User {request.Name} created!";
}
}
Özetlemek gerekirse, yazma işlemlerinde Command’lar tanımlıyoruz ve okuma işlemlerinde ise Query’ler tanımlıyoruz. Bu arada MediatR paketini kullanmak gibi bir zorunluluğumuz yok, kendimizde manuel olarak uygulayabiliriz... İki konuyu detaylıca anlattığımıza göre ana konumuza artık giriş yapa biliriz.
MediatR Pipeline Behavior Nedir?
Danışmanlık verdiğim birçok şirkette, projelerde CQRS ve MediatR ikilisinin birlikte kullanıldığına şahit oldum. Ancak çoğu projede, bu yapıların yalnızca Command ve Query ayrımı üzerinden benimsendiğini, yani temelde kod organizasyonu amacıyla kullanıldığını gözlemledim. Oysa CQRS’in temel felsefesi, okuma (Query) ve yazma (Command) işlemlerinin yalnızca mantıksal olarak değil, aynı zamanda fiziksel olarak da ayrılmasıdır. Bu ideal senaryoda, okuma ve yazma işlemlerinin farklı veri modelleri ve hatta farklı veri tabanları üzerinden yürütülmesini gerektirir. Neyse linç yemeyelim şimdi 😃 konumuza dönebiliriz..
Giriş
MediatR, .NET ekosisteminde CQRS yaklaşımını benimseyen projelerde sıklıkla kullanılan bir kütüphanedir. Temel hedefi loosely coupled (gevşek bağlı) bir yapı oluşturarak isteklerin bir handler üzerinden işlenmesini sağlamaktır.
Bu yapının en güçlü özelliklerinden biride Pipeline Behaviors’tır. Pipeline Behavior’lar, bir request işlenmeden önce, işlenme sırasında ve işlendikten sonra devreye girerek, araya girip bazı işlemleri merkezi bir şekilde gerçekleştirmemizi sağlar.
Pipeline Behavior Ne İşe Yarar?
MediatR Pipeline Behavior bir Command ya da Query handler’a ulaşmadan önce veya sonra ortak işlemler yapmak istediğimizde her yere aynı kodu yazmak yerine bu işlemleri bir behavior içerisine alırız. Yazılım mimarilerinde sıklıkla karşılaşılan Cross-Cutting Concern’leri behavior’larla merkezi bir yapıda ele alarak, kod tekrarını azaltır, sistemin tutarlılığını artırır ve sürdürülebilir bir mimari inşa etmiş oluruz.
Sık Kullanılan Behavior’lar:
- Validasyon (ValidationBehavior)
- Logging (LoggingBehavior)
- Cache (CachingBehavior)
- Transaction (TransactionBehavior)
- Exception Handling (ExceptionBehavior)
Pipeline Behavior Nasıl Yazılır?
Bir Pipeline Behavior yazmak için IPipelineBehavior<TRequest, TResponse> arayüzünü implement etmemiz gerekir.
Örnek:
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;
public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
}
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
_logger.LogInformation($"[START] Handling {typeof(TRequest).Name}");
var response = await next(); // handler’a geçiş
_logger.LogInformation($"[END] Handled {typeof(TRequest).Name}");
return response;
}
}
Pipeline Behavior Nasıl Calisir?
Çalışma mantığını yukarıda verdiğim örnek üzerinden detaylıca anlatmak hem daha kalıcı hemde daha sağlıklı olacaktır...
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
Bu method'un amacı, bir MediatR pipeline behavior olarak her bir istek (request) işlendiğinde öncesinde ve sonrasında loglama yapmaktır.
- TRequest: Gelen komut veya sorgu nesnesi (örnek: CreateUserCommand).
- TResponse: Komutun/sorgunun handler'ı tarafından dönecek cevap tipi (örnek: UserDto veya bool).
- next(): Pipeline’da bir sonraki adıma (yani handler’a) geçiş yapan fonksiyon.
- cancellationToken: Async işlemlerde iptal için kullanılan standart parametre.
Adım 1: Loglama - Başlangıç
_logger.LogInformation($"[START] Handling {typeof(TRequest).Name}");
- Request işlenmeye başlamadan önce, konsola (veya başka bir log sistemine) şu formatta bir log yazar:
[START] Handling CreateUserCommand
Adım 2: Handler’a Geçiş
var response = await next();
- Burada next() çağrısı yapılır ve pipeline’daki bir sonraki adım çalıştırılır.
- Bu genellikle Handler sınıfıdır, yani işin asıl yapıldığı yer.
Adım 3: Loglama - Bitiş
_logger.LogInformation($"[END] Handled {typeof(TRequest).Name}");
- İşlem tamamlandıktan sonra bir log daha atılır:
[END] Handled CreateUserCommand
Adım 4: Cevabı Döndür
return response;
- Handler'dan dönen cevap, pipeline'a geri döndürülür.
Özet:
- Bu yapı, MedaitR’da Pipeline Behavior olarak geçer ve tüm istekler için merkezi bir log mekanizması sağlar.
- Bu yapı, sistemin davranışını dışarıdan izlenebilir hale getirir.
- Bu yapı, production ortamında sorun çözme sürecini kolaylaştırır.
DI Konfigürasyonu
Behavior’ları kullanabilmek için Startup.cs, Program.cs yada kendi container’ımıza ekleye biliriz.
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(CachingBehavior<,>)); // örnek
Evet dostlar, yukarıda ki kod satırlarına baktığımızda her Command ve Query çağrısında bu behavior’lar sırasıyla çalışır.
Bahavior’lar, middleware mantığına benzer şekilde çalışır. Sırayla birbirlerine Next() diyerek ilerlerler.
Request --> ValidationBehavior --> LoggingBehavior --> Handler
<-- return response
İlk tanımlanan behavior en dışta olur, en son tanımlanan behavior ise handler’a en yakındır.
Not: Yapay zeka çağında olduğumuz bu dönemde, sizlere daha fazla şeffaflık sunmak adına artık her makalemin sonunda orijinallik kontrolüne ait ekran görüntüsünü paylaşacağım.
Dilerseniz siz de makale linkini aşağıdaki araç üzerinden kontrol ederek, içeriğin yapay zeka tarafından mı yoksa insan eliyle mi yazıldığını görebilirsiniz:
https://writer.com/ai-content-detector/

Evet Dostlar, burada en sık kullanılan behavior’ları tek tek örneklemedim çünkü temel amacın neden ve nasılısını detaylandırmaktı. Bir sonraki makalede görüşmek dileğiyle, kendinize iyi bakın.
İyi Çalışmalar..
Kaynakça
Design Patterns: https://en.wikipedia.org/wiki/Design_Patterns
Microsoft: https://learn.microsoft.com/en-us/azure/architecture/patterns/cqrs
MediatR: https://github.com/jbogard/MediatR