> ## 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.

# BuildingBlocks.Specification

> Ardalis.Specification üzerine query kapsülleme, in-memory kombinatörler, filtre ve cache entegrasyonu.

`BuildingBlocks.Specification`, `Ardalis.Specification` 9.x üzerine kurulu ince bir katmandır. Sorgu mantığını (where/include/orderby/paging) repository'den ayrı, test edilebilir ve **cache'lenebilir** spec sınıflarına taşır. İki ek değer sunar: in-memory mantıksal kombinatörler (`And`/`Or`/`Not`) ve spec'e doğrudan cache tag/TTL iliştirme.

<Info>
  Repository altyapısı (`EFRepository`, `CachedRepository`, `ListAsync`) ve hibrit cache davranışı için bkz. [Veri Katmanı](/data/overview) ve [Cache](/building-blocks/caching).
</Info>

## `CompositeSpecification<T>`

Spec'lerin türetildiği temel sınıf. `Ardalis.Specification.Specification<T>`'i genişletir ve in-memory değerlendirme için `IInMemorySpecification<T>` sağlar.

```csharp theme={null}
public abstract class CompositeSpecification<T>
    : Ardalis.Specification.Specification<T>, IInMemorySpecification<T>
{
    public abstract override bool IsSatisfiedBy(T candidate);

    public IInMemorySpecification<T> And(IInMemorySpecification<T> other);
    public IInMemorySpecification<T> AndNot(IInMemorySpecification<T> other);
    public IInMemorySpecification<T> Or(IInMemorySpecification<T> other);
    public IInMemorySpecification<T> OrNot(IInMemorySpecification<T> other);
    public IInMemorySpecification<T> Not();
}
```

| Üye                                       | Amaç                                                                                   |
| ----------------------------------------- | -------------------------------------------------------------------------------------- |
| `IsSatisfiedBy(T)`                        | Bir adayın (DB'ye gitmeden) spec'i karşılayıp karşılamadığını döner                    |
| `And` / `AndNot` / `Or` / `OrNot` / `Not` | İki spec'i in-memory mantıksal olarak birleştirir (`AndSpecification<T>` vb. döndürür) |

`Query.*` zinciri (`Where`, `Include`, `OrderBy`, `Skip`, `Take`) Ardalis'ten gelir ve EF Core'a çevrilir; `IsSatisfiedBy` ise aynı kuralı bellekte de uygulanabilir kılar.

## Filtreler — `IFilter` / `FilterBase`

API'den gelen liste/arama parametrelerini standartlaştıran kontrat. `FilterBase` türetilerek aggregate'e özel filtre yapılır.

| Üye                     | Tip      | Açıklama                                                                            |
| ----------------------- | -------- | ----------------------------------------------------------------------------------- |
| `HasTenant`             | `bool`   | `TenantId.HasValue`                                                                 |
| `IsPagingEnabled`       | `bool`   | `PageSize > 0 && Page >= 0`                                                         |
| `HasSearch` / `HasSort` | `bool`   | `SearchTerm` / `SortBy` dolu mu                                                     |
| `Page` / `PageSize`     | `int`    | Sayfalama (0-based — bkz. uyarı)                                                    |
| `Skip` / `Take`         | `int`    | `PaginationHelper.CalculateSkip/Take`'ten hesaplanır                                |
| `TenantId`              | `Guid?`  | Multi-tenant filtre                                                                 |
| `OrderBy`               | `string` | `"asc"` / `"desc"` — `OrderByASC`/`OrderByDESC` türetilir                           |
| `SortBy`                | `string` | Sıralanacak alan adı                                                                |
| `SearchTerm`            | `string` | Arama; `IsSearchTermNumeric`/`IsSearchTermTckn`/`IsSearchTermDateTime` yardımcıları |

<Warning>
  Sayfalama **0-based**'tir: `IsPagingEnabled` koşulu `Page >= 0` ister ve `PaginationHelper` 0'ıncı sayfayı ilk sayfa kabul eder. Frontend `useState(0)` ile başlamalı; `page=1` göndermek ilk sayfayı sessizce atlar.
</Warning>

## Cache entegrasyonu

Cache, Ardalis'in mevcut `Query.EnableCache(name, args)` zinciriyle açılır. Paket buna iki fluent uzantı ekler:

| Uzantı                      | Etki                                                                                                                          |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `WithTags(params string[])` | Spec'e invalidation tag'leri iliştirir (`SetCacheTags`) — `CachedRepository` bunları `HybridCache.GetOrCreateAsync`'e geçirir |
| `WithCacheTtl(TimeSpan)`    | Spec'e per-entry TTL iliştirir (`SetCacheTTL`) — `HybridCacheEntryOptions.Expiration`'a yansır                                |

<Note>
  Ad bilerek `WithCacheTtl`'dir — Ardalis'in `WithTimeToLive`'i ile ambiguous reference olmasın diye. Aynı şekilde paralel `ICacheSpecificationBuilder<T>` kaldırılmıştır; `Ardalis.Specification.ICacheSpecificationBuilder<T>` kullanılır.
</Note>

## Kullanım — spec tanımı + Repository.ListAsync

```csharp theme={null}
public sealed class GetActiveFaqsSpecification : CompositeSpecification<Faq>
{
    public GetActiveFaqsSpecification(Guid? tenantId)
    {
        Query
            .Where(f => f.IsActive)
            .OrderBy(f => f.Order)
            .EnableCache(nameof(GetActiveFaqsSpecification), tenantId ?? Guid.Empty)
            .WithCacheTtl(TimeSpan.FromMinutes(10))
            .WithTags("faq", $"tenant:{tenantId}");
    }

    // In-memory değerlendirme (ör. domain event sonrası filtre)
    public override bool IsSatisfiedBy(Faq candidate) => candidate.IsActive;
}
```

```csharp theme={null}
// Read repository (CachedRepository decorator) ile — sonuç cache'lenir
var spec = new GetActiveFaqsSpecification(tenantId);
IReadOnlyList<Faq> faqs = await _readRepository.ListAsync(spec, ct);

// Aggregate değiştiğinde aynı tag düşürülür (domain event handler içinde)
await _cache.RemoveByTagAsync("faq", ct);
```

```csharp theme={null}
// In-memory kombinasyon — DB'ye gitmeden iki kuralı birleştir
IInMemorySpecification<User> activeAndVerified =
    new ActiveUserSpec().And(new PhoneVerifiedSpec());

bool ok = ((CompositeSpecification<User>)activeAndVerified).IsSatisfiedBy(user);
```

## İlgili

<CardGroup cols={2}>
  <Card title="Veri katmanı" icon="database" href="/data/overview">
    EFRepository, CachedRepository ve SpecificationEvaluator.
  </Card>

  <Card title="Cache" icon="layer-group" href="/building-blocks/caching">
    EnableCache/WithTags ile hibrit cache entegrasyonu.
  </Card>

  <Card title="Domain" icon="cube" href="/domain/overview">
    Spec'lerin aggregate başına organizasyonu.
  </Card>

  <Card title="Invalidation" icon="eraser" href="/caching/invalidation">
    Tag-based eviction ve domain event tetikleme.
  </Card>
</CardGroup>
