Skip to main content
Cache, spec seviyesinde etkinleştirilir ve tag bazlı invalidate edilir. Bir aggregate güncellendiğinde ilgili domain event bir handler’ı tetikler; handler IHybridRequestCache.RemoveByTagAsync(tag) çağırır. HybridCache hem L1 (in-memory) hem L2 (Redis) girdilerini düşürür; çoklu-instance kurulumda ayrıca diğer node’lara CacheInvalidationIntegrationEvent broadcast edilir. Bu sayede güncelleme bir node’da yapılsa da tüm node’ların L1’i temizlenir.
İlgili dosyalar: Application/DomainEventHandlers/Faq{Updated,Created,Deleted,Reordered}/InvalidateFaqCacheDomainEventHandler.cs · Application/SeedWork/Caching/CacheTags.cs · Application/IntegrationEventHandlers/CacheInvalidation/CacheInvalidationIntegrationEventHandler.cs · Domain/AggregatesModel/FaqAggregate/Specifications/PublicFaqsPagedSpecification.cs

Çalışma prensibi (FAQ örneği)

Public FAQ listesi sık çağrılır, değişimi nettir; bu yüzden cache’i hardcoded açıktır. Spec, sorguyu tag’ler:
// PublicFaqsPagedSpecification
public const string CacheTag = "faqs";

Query.EnableCache(
        nameof(PublicFaqsPagedSpecification),
        filter.Page, filter.PageSize, filter.SearchTerm)
     .WithCacheTtl(TimeSpan.FromMinutes(5))
     .WithTags(CacheTag);
Tüm sayfa/sıralama/arama varyantları aynı faqs tag’i altında toplanır. Tek bir RemoveByTagAsync("faqs") hepsini lazy invalidate eder. Tag string’i tek yerde sabitlenir ki spec ile handler typo’suz paylaşsın:
// CacheTags
public static class CacheTags
{
    public const string Faqs = PublicFaqsPagedSpecification.CacheTag;
}

Sequence — güncelleme + çoklu-instance broadcast

Adım adım — invalidate

1

Aggregate güncellenir, event yayılır

Komut handler aggregate metodunu çağırır; metot içeride AddDomainEvent(new FaqUpdatedDomainEvent(faqId, isActive)) biriktirir. SaveEntitiesAsync sonrası event dispatch edilir. Event yalın bir veri taşıyıcısıdır:
public class FaqUpdatedDomainEvent : DomainEvent, INotification
{
    public Guid FaqId    { get; }
    public bool IsActive { get; }
    public FaqUpdatedDomainEvent(Guid faqId, bool isActive) { FaqId = faqId; IsActive = isActive; }
}
2

Handler tag'i düşürür

InvalidateFaqCacheDomainEventHandler event’i dinler ve tag’i temizler. Hata cache invalidasyonunu fail-safe yapar — loglanır ama isteği bozmaz:
public sealed class InvalidateFaqCacheDomainEventHandler
    : INotificationHandler<FaqUpdatedDomainEvent>
{
    private readonly IHybridRequestCache _cache;
    // ctor ...

    public async Task Handle(FaqUpdatedDomainEvent e, CancellationToken ct)
    {
        try
        {
            await _cache.RemoveByTagAsync(CacheTags.Faqs, ct);
            _logger.LogDebug("[CacheInvalidate] FaqUpdated → {Tag} dropped (faqId={FaqId})", CacheTags.Faqs, e.FaqId);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "[CacheInvalidate] FaqUpdated failed: faqId={FaqId}", e.FaqId);
        }
    }
}
Aynı tag için ayrı handler dosyaları vardır: FaqCreated, FaqUpdated, FaqDeleted, FaqReordered. Hepsi Faqs tag’ini düşürür. Konvansiyon: handler’lar Application/DomainEventHandlers/<EventName>/InvalidateFaqCacheDomainEventHandler.cs altında, tek class tek dosya.
3

Çoklu-instance broadcast

RemoveByTagAsync lokal L1 + L2’yi düşürür ve CacheInvalidationIntegrationEvent’i (Cache:BroadcastTagInvalidation açıksa) yayar. Diğer node’lar bunu tüketip yalnızca kendi L1’lerini temizler. Echo döngüsü SourceNodeId ile engellenir:
public async Task Handle(CacheInvalidationIntegrationEvent @event)
{
    if (@event.SourceNodeId == EventBusRemoteTagBroadcaster.NodeId)
        return; // kendi yayınımız — atla (echo guard)

    foreach (var tag in @event.Tags)
        await _cache.RemoveByTagLocallyAsync(tag);  // sadece local L1, tekrar broadcast YOK
}
RemoveByTagLocallyAsync tekrar broadcast yapmaz; bu da sonsuz döngüyü önler. Detay: Çoklu-instance cache.
Kayıt sırası kritiktir: AddRedisCache (IDistributedCache) önce, sonra AddCache, sonra AddHybridCache. Aksi halde L2 (Redis) devre dışı kalır ve cache yalnızca L1-only çalışır — node’lar arası tutarsızlık oluşur. Bkz. HybridCache.

Yeni bir cache’lenebilir query eklemek

Yeni bir public/sık-okunan liste için cache açma sırası:
1

Spec'te cache'i etkinleştir

İlgili Specification<T> içinde sorguyu tag’leyin:
public sealed class PublicCentersPagedSpecification : Specification<Center>
{
    public const string CacheTag = "centers";

    public PublicCentersPagedSpecification(PublicCentersFilter filter)
    {
        Query.Where(c => c.DeletedAt == null && c.IsActive);
        // ... where / orderby / paging ...
        Query.AsNoTracking();
        Query.EnableCache(nameof(PublicCentersPagedSpecification), filter.Page, filter.PageSize, filter.SearchTerm)
             .WithCacheTtl(TimeSpan.FromMinutes(5))
             .WithTags(CacheTag);
    }
}
2

Tag'i CacheTags'e ekle

public static class CacheTags
{
    public const string Faqs    = PublicFaqsPagedSpecification.CacheTag;
    public const string Centers = PublicCentersPagedSpecification.CacheTag; // yeni
}
Tek kaynak: spec’teki const string’i referans alın, elle string yazmayın.
3

Domain event(ler)i yay

Center aggregate’inin değiştiren metodlarında ilgili domain event’i (CenterUpdatedDomainEvent vb.) AddDomainEvent ile yayın. Bkz. Yeni Domain Event.
4

Invalidation handler'ı yaz

Application/DomainEventHandlers/CenterUpdated/InvalidateCenterCacheDomainEventHandler.cs:
public sealed class InvalidateCenterCacheDomainEventHandler
    : INotificationHandler<CenterUpdatedDomainEvent>
{
    private readonly IHybridRequestCache _cache;
    public async Task Handle(CenterUpdatedDomainEvent e, CancellationToken ct)
        => await _cache.RemoveByTagAsync(CacheTags.Centers, ct);
}
Read query bir sonraki çağrıda DB’den taze yüklenip yeniden cache’lenir.
Şu an Bağış/Etkinlik public listelerinde cache bilinçli olarak yoktur — değişiklikler anında görünsün diye. Ölçek gerektiğinde aynı reçete (spec’te EnableCache + WithTags, CacheTags’e tag, invalidation handler) uygulanır.

İlgili

HybridCache

L1/L2 yapısı ve DI kayıt sırası.

Tag Invalidation

Tag mekaniği derinlemesine.

Çoklu-instance

Broadcast ve echo guard.

Yeni Domain Event

Invalidation’ı tetikleyen event’i ekleme.