Ana içeriğe geç
Ahmet Faruk UzunkayaYaratıcı geliştirici
← Projelere dön
Arka uçTamamlandıGüncellendi · 1 Ekim 2026

Demo Social Media API

CQRS, SignalR ile sohbet ve MinIO'ya dosya yükleme içeren, katmanlı bir .NET 10 sosyal medya API'si.

Katmanlı bir .NET 10 sosyal medya API örneği. Kimlik doğrulama, arkadaşlık, gönderi / yorum / beğeni / kayıt, MinIO ile dosya yükleme ve SignalR ile gerçek zamanlı sohbet var. Yapıyı CQRS + MediatR üzerine kurdum.

.NET 10CQRSMediatRSignalRPostgreSQLMinIO
Durum
Tamamlandı
Kategori
Arka uç
Güncelleme
1 Ekim 2026
Yığın
.NET 10CQRSMediatRSignalRPostgreSQLMinIO
Demo Social Media API

Demo Social Media API, .NET 10 ile yazdığım katmanlı bir sosyal medya arka ucu örneği. Bu API'ye bağlanan bir istemci kayıt olup giriş yapabiliyor, kullanıcı arayıp arkadaşlık isteği gönderebiliyor, gönderi paylaşıp yorum yazabiliyor. Gönderileri beğenip kaydetmek, birebir ya da grup odalarında sohbet etmek de mümkün. Görseller MinIO'ya yükleniyor, sohbet mesajları SignalR ile anlık olarak dağıtılıyor, veriler de EF Core aracılığıyla PostgreSQL'de duruyor.

Akış anonim olarak da okunabiliyor: son 50 gönderi dönüyor, oturum açıksa her gönderide isLiked ve isSaved alanları da dolu geliyor. Kullanıcı ararken nickname'in içinde geçen ifadeler eşleşiyor, e-postada ise yalnızca tam eşleşme kabul ediliyor. Arayan kişinin kendisi ve arkadaşları sonuçlarda çıkmıyor. Kimse kendine ya da arasında zaten bir istek veya arkadaşlık olan birine istek gönderemiyor.

İstek hangi yoldan geçiyor

Projeyi dört katmana ayırdım. İstekler MediatR üzerinden, CQRS düzeninde komut ve sorgu handler'larına gidiyor:

DemoSocialMedia.Api              Controller'lar, ChatHub, ApiExceptionHandler, Swagger
      │  IMediator.Send(komut | sorgu)
      ▼
DemoSocialMedia.Application      Komut / sorgu handler'ları, DTO'lar, FluentValidation, AppException
      ▼
DemoSocialMedia.Domain           Saf entity'ler
      ▲
DemoSocialMedia.Infrastructure   EF Core AppDbContext, entity konfigürasyonları, migration'lar, MinIO

Controller'larda iş mantığı yok. Her biri isteği bir komuta (CreatePost, ToggleLike, SendFriendRequest, SendMessage gibi) ya da bir sorguya (GetFeed, GetFriends, GetMessages gibi) çevirip IMediator'a iletmekle yetiniyor. Domain katmanı User, Post, Comment, Like, Save, FriendRequest, Friendship, ChatRoom, ChatRoomMember, Message ve EmailVerificationToken entity'lerinden oluşuyor; her birinin EF Core konfigürasyonu Infrastructure'da kendi sınıfında duruyor.

Hatalar ProblemDetails biçiminde dönüyor. Doğrulama hatalarını FluentValidation'ın otomatik doğrulaması yakalıyor ve 400 ValidationProblemDetails olarak döndürüyor. İş kuralı ihlallerini ise Application katmanında, durum koduyla birlikte bir AppException olarak fırlatıyorum. Örneğin kayıtta e-posta küçük harfe çevriliyor; aynı e-posta ya da nickname zaten varsa handler Conflict durumlu bir AppException fırlatıyor. Bu istisnaları Api katmanındaki ApiExceptionHandler yakalıyor.

Token hem başlıkta hem cookie'de

Giriş yapıldığında JWT, HttpOnly bir token cookie'sine yazılıyor. JwtBearerEvents.OnMessageReceived içinde token'ı önce Authorization: Bearer başlığında arıyorum, başlık yoksa bu cookie'ye bakıyorum. Bu sayede aynı API hem cookie kullanan bir tarayıcı istemcisiyle hem de token'ı kendisi gönderen istemcilerle çalışabiliyor. Kayıt sırasında bir e-posta doğrulama token'ı da üretiliyor, ama e-posta gönderimi şimdilik yalnızca log'a yazan bir mock servis.

Dosya yükleme yalnızca JPEG, PNG, GIF ve WebP kabul ediyor ve 5 MB ile sınırlı. Yükleme sonunda bir objectName dönüyor; gönderi oluştururken imageUrl olarak bu değer veriliyor. Development ortamında bucket ilk oluşturulurken anonim okumaya açıldığı için düz bir public URL dönüyor. Diğer ortamlarda ise 1 saat geçerli bir presigned URL geliyor.

Mesaj REST'ten gidiyor, SignalR'dan geliyor

Sohbette yazma ve dinleme kanallarını birbirinden ayırdım. Mesaj POST /api/chat/rooms/{roomId}/messages ile gönderiliyor. ChatController önce gönderenin odanın üyesi olup olmadığına bakıyor, sonra mesajı kaydedip odanın ChatHub grubuna ReceiveMessage olarak yayınlıyor. Hub'ı (/chathub) yalnızca dinlemek için kullanıyorum: istemci JoinRoom(roomId) ile odaya katılıyor, üye değilse HubException alıyor, LeaveRoom ile de ayrılıyor. Gönderenin kimliği token'dan alındığı için kimse başkası adına mesaj yazamıyor.

Mesaj geçmişini de yalnızca odanın üyeleri okuyabiliyor. Aynı iki kişi için birebir oda oluşturma isteği idempotent.

Projede ASP.NET Core, SignalR, MediatR, FluentValidation, EF Core ile Npgsql, MinIO, BCrypt ve Swagger kullandım; her şey Docker Compose ile ayağa kalkıyor. Açıkçası bitmemiş bir parça da var: ApiExceptionHandler içinde istisna türünü durum koduna eşleyen kısım kodda hâlâ TODO. Şu haliyle, AppException dahil handler'a ulaşan her istisna 500 ProblemDetails olarak dönüyor. Yani yukarıdaki Conflict gibi durum kodları henüz istemciye ulaşmıyor.