> ## Documentation Index
> Fetch the complete documentation index at: https://docs.diyanet.pro/llms.txt
> Use this file to discover all available pages before exploring further.

# Cache Invalidation

> Tag-bazlı invalidation, CacheTags sabitleri ve domain event handler örnekleri

## Tag-bazlı invalidation

Cache entry'leri key bilmeden, **tag** üzerinden invalidate edilir. Bir spec birden çok varyant (sayfa, arama, sıralama) üretir; hepsi aynı tag altında toplandığı için tek `RemoveByTagAsync(tag)` çağrısı tümünü düşürür.

`HybridCache` tag invalidation'ı **lazy** uygular: L2'de (Redis) tag bir timestamp ile işaretlenir; bu tag'e ait varolan entry'ler bir sonraki erişimde geçersiz sayılır. Tek tek silme (scan + DEL) yapılmaz.

## `CacheTags` sabitleri

Spec ile domain event handler'ın aynı string'i typo'suz paylaşması için tag'ler `CacheTags` sınıfında merkezîleştirilir (`src/.../Application/SeedWork/Caching/CacheTags.cs`). Tek kaynak, tek doğruluk:

```csharp theme={null}
public static class CacheTags
{
    // Spec'teki sabitten türetilir — spec ve handler aynı değeri görür.
    public const string Faqs = PublicFaqsPagedSpecification.CacheTag;   // "faqs"
}
```

<Note>
  Bagis/Etkinlik aggregate'leri için cache şu an **yok** (anlık değişiklik görünsün diye). Public liste ölçeklenince ilgili spec'lere `EnableCache` + buraya tag eklenir.
</Note>

## Domain event handler'da invalidation

Aggregate değiştiğinde domain event yayar; ilgili handler tag'i düşürür. FAQ örneği — `InvalidateFaqCacheDomainEventHandler` (`src/.../Application/DomainEventHandlers/FaqCreated/`):

```csharp theme={null}
public sealed class InvalidateFaqCacheDomainEventHandler
    : INotificationHandler<FaqCreatedDomainEvent>
{
    private readonly IHybridRequestCache _cache;
    private readonly ILogger<InvalidateFaqCacheDomainEventHandler> _logger;

    public InvalidateFaqCacheDomainEventHandler(
        IHybridRequestCache cache,
        ILogger<InvalidateFaqCacheDomainEventHandler> logger)
    {
        _cache  = cache;
        _logger = logger;
    }

    public async Task Handle(FaqCreatedDomainEvent e, CancellationToken ct)
    {
        try
        {
            await _cache.RemoveByTagAsync(CacheTags.Faqs, ct);
            _logger.LogDebug("[CacheInvalidate] FaqCreated → {Tag} dropped (faqId={FaqId})", CacheTags.Faqs, e.FaqId);
        }
        catch (Exception ex)
        {
            // Cache hatası ana akışı durdurmaz: defansif try/catch + warn log.
            _logger.LogWarning(ex, "[CacheInvalidate] FaqCreated failed: faqId={FaqId}", e.FaqId);
        }
    }
}
```

FAQ aggregate'inin dört event'i ayrı handler dosyalarında aynı tag'i düşürür — tek tag (`faqs`) tüm liste varyantlarını yakalar:

| Domain event              | Handler dizini                      | Düşürülen tag    |
| ------------------------- | ----------------------------------- | ---------------- |
| `FaqCreatedDomainEvent`   | `DomainEventHandlers/FaqCreated/`   | `CacheTags.Faqs` |
| `FaqUpdatedDomainEvent`   | `DomainEventHandlers/FaqUpdated/`   | `CacheTags.Faqs` |
| `FaqDeletedDomainEvent`   | `DomainEventHandlers/FaqDeleted/`   | `CacheTags.Faqs` |
| `FaqReorderedDomainEvent` | `DomainEventHandlers/FaqReordered/` | `CacheTags.Faqs` |

<Tip>
  Invalidation domain event handler'da yapılır çünkü hangi tag'in etkilendiğini en iyi domain bilir. Yazma (Create/Update/Delete) komutu aggregate metodunu çağırır, aggregate event yayar, `SaveEntitiesAsync` sonrası dispatch edilir (bkz. [Domain Events](/events/domain-events)) ve cache handler tag'i düşürür. Komut handler cache hakkında hiçbir şey bilmez.
</Tip>

## `RemoveByTagAsync` vs `RemoveByTagLocallyAsync`

`HybridRequestCache` (`BuildingBlocks.Caching/HybridRequestCache.cs`) iki metodu farklı amaçla taşır:

```csharp theme={null}
public async ValueTask RemoveByTagAsync(string tag, CancellationToken ct = default)
{
    await _cache.RemoveByTagAsync(tag, ct);   // local L1 + L2

    if (_options.CurrentValue.BroadcastTagInvalidation)
        // Diğer node'ların L1'i için broadcast (best-effort; fail olsa runtime kırılmaz).
        await _broadcaster.BroadcastInvalidationAsync(new[] { tag }, ct);
}

// Broadcast'ten geldiği için tekrar broadcast YAPMAZ — loop önleme.
public ValueTask RemoveByTagLocallyAsync(string tag, CancellationToken ct = default)
    => _cache.RemoveByTagAsync(tag, ct);
```

| Metod                     | Local L1/L2 | Broadcast         | Kullanım                                                             |
| ------------------------- | ----------- | ----------------- | -------------------------------------------------------------------- |
| `RemoveByTagAsync`        | ✅           | ✅ (config açıksa) | **Origin** — değişikliği yapan node. Domain event handler'larda.     |
| `RemoveByTagLocallyAsync` | ✅           | ❌                 | **Receiver** — broadcast'i alan diğer node'lar. Cascade/loop önleme. |

Receiver tarafı (`CacheInvalidationIntegrationEventHandler`) `RemoveByTagLocallyAsync` kullanır; aksi halde her node aldığı broadcast'i tekrar yayınlar ve sonsuz cascade oluşur. Ayrıntı: [Multi-Instance Senkron](/caching/multi-instance).

## İlgili

<CardGroup cols={2}>
  <Card title="Multi-Instance Senkron" icon="network-wired" href="/caching/multi-instance">
    Broadcast akışı ve echo guard.
  </Card>

  <Card title="Domain Events" icon="bolt" href="/events/domain-events">
    Event yayını ve MediatR dispatch.
  </Card>

  <Card title="Hybrid Cache" icon="layer-group" href="/caching/hybrid-cache">
    Spec cache ve `IHybridRequestCache` API.
  </Card>

  <Card title="Cache Mimarisi" icon="database" href="/caching/overview">
    Genel bakış.
  </Card>
</CardGroup>
