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

# SharedKernel

> DDD yapı taşları: EntityBase, ValueObject, Enumeration, DomainEvent, guard altyapısı ve repository arayüzleri.

`DiyanetCleanArchitecture.Domain.SharedKernel` projesi, tüm domain modelinin üzerine inşa edildiği **temel yapı taşlarını** içerir. Burada somut iş kuralı yoktur; yalnızca soyut taban sınıflar, arayüzler ve yardımcılar bulunur.

```text theme={null}
SharedKernel/
├── SeedWork/      → EntityBase, Entity, ValueObject, Enumeration, DomainEvent,
│                    IAggregateRoot, IRepository, IUnitOfWork, IAuditableEntity...
├── ValueObjects/  → FullName, Email, Phone, OtpCode, TCKimlikNo, TotpSecret...
├── Enumerations/  → OtpType, AuthProviderType, City...
└── Utils/         → Check, GuidFactory, MobilePhoneUtility...
```

## EntityBase\<TId>

Tüm entity'lerin tabanı. Kimlik, audit izi ve soft-delete alanlarını tek yerde toplar. Setter'lar `protected`'tır — değer yalnızca aggregate davranışı veya interceptor üzerinden değişir.

```csharp theme={null}
public abstract class EntityBase<TId> : EntityBase, IEntityBase<TId>, IAuditableEntity, ISoftDeletable
{
    public virtual TId Id { get; protected set; }

    // IAuditableEntity — interceptor doldurur
    public virtual DateTime? CreatedAt { get; protected set; }
    public virtual DateTime? UpdatedAt { get; protected set; }
    public virtual Guid?     CreatedBy { get; protected set; }
    public virtual Guid?     UpdatedBy { get; protected set; }

    // ISoftDeletable — interceptor doldurur
    public virtual DateTime? DeletedAt { get; protected set; }
    public virtual Guid?     DeletedBy { get; protected set; }

    public virtual bool IsDeleted()   => DeletedAt is not null;
    public virtual bool IsTransient() => Id.Equals(default(TId));
    // ... Equals / GetHashCode / == / != kimlik bazlı
}
```

* **`IsDeleted()`** — `DeletedAt` dolu ise kayıt mantıksal olarak silinmiştir. Global query filter (`DeletedAt == null`) bu kayıtları sorgulardan otomatik dışlar.
* **`IsTransient()`** — henüz kalıcı kimliği olmayan (DB'ye yazılmamış) nesneyi belirtir. `Equals`/`GetHashCode` kimlik üzerinden çalışır; iki transient entity asla eşit kabul edilmez.

`EntityBase` (generic olmayan taban) domain event kuyruğunu tutar:

```csharp theme={null}
public abstract class EntityBase : IEntityBase
{
    private List<INotification> _domainEvents;
    public IReadOnlyCollection<INotification> DomainEvents => _domainEvents?.AsReadOnly();

    public void AddDomainEvent(INotification eventItem) { /* listeye ekler */ }
    public void RemoveDomainEvent(INotification eventItem) { /* ... */ }
    public void ClearDomainEvents() { /* ... */ }
}
```

### Entity : EntityBase\<Guid>

Pratikte aggregate ve çocuk entity'lerin neredeyse tamamı `Guid` kimliklidir, bu yüzden hazır bir kısayol vardır:

```csharp theme={null}
public abstract class Entity : EntityBase<Guid>
{
    public override bool IsTransient() => base.IsTransient() || CreatedAt == default;
}
```

<Note>
  `Entity` için `IsTransient`, `CreatedAt` set edilene kadar nesneyi geçici sayar — böylece henüz persist edilmemiş (event'i daha dispatch edilmemiş) nesneler eşitlik kıyaslamasında doğru davranır.
</Note>

## IAggregateRoot

Boş bir **marker** arayüzüdür. Tutarlılık sınırının kökünü işaretler; `IRepository<T>` yalnızca `IAggregateRoot` türlerini kabul eder.

```csharp theme={null}
public interface IAggregateRoot {}
```

## ValueObject

Kimliği olmayan, **değerine göre eşitlenen** nesnelerin tabanı. Türetilen sınıf yalnızca `GetAtomicValues()` ile eşitliğe katkıda bulunan alanları sıralar; `Equals`, `GetHashCode` ve operatörler taban tarafından sağlanır.

```csharp theme={null}
public abstract class ValueObject
{
    protected abstract IEnumerable<object> GetAtomicValues();

    public override bool Equals(object obj)
    {
        if (obj == null || obj.GetType() != GetType()) return false;
        var other = (ValueObject)obj;
        // GetAtomicValues() sırayla karşılaştırılır
    }

    public override int GetHashCode()
        => GetAtomicValues().Select(x => x?.GetHashCode() ?? 0).Aggregate((x, y) => x ^ y);
}
```

Örnek bir VO yalnızca atomik değerlerini bildirir:

```csharp theme={null}
public sealed class FullName : ValueObject
{
    public string Value { get; }
    // ... ctor validasyonu ...
    protected override IEnumerable<object> GetAtomicValues()
    {
        yield return Value;
    }
}
```

## DomainEvent

Tüm domain event'lerin tabanı. Her event **Version 7 (zaman sıralı) GUID** kimliği ve UTC zaman damgası alır. `INotification` ile birlikte kullanıldığında MediatR pipeline'ına girer.

```csharp theme={null}
public abstract class DomainEvent
{
    public string Type => this.GetType().Name;
    public readonly Guid Id;
    public readonly Guid CorrelationID;
    public readonly DateTime CreatedAt;

    public DomainEvent()
    {
        Id = Guid.CreateVersion7(DateTime.UtcNow);
        CreatedAt = DateTime.UtcNow;
    }
    public DomainEvent(Guid correlationID) : this() => this.CorrelationID = correlationID;
}
```

Somut bir event hem `DomainEvent`'ten türer hem `INotification`'ı uygular:

```csharp theme={null}
public class UserCreatedDomainEvent : DomainEvent, INotification
{
    public Guid UserId { get; }
    public FullName? FullName { get; }
    // ...
}
```

## Enumeration

C# `enum`'larının yetersiz kaldığı yerlerde kullanılan, **davranış ve veri taşıyabilen** tip-güvenli sabit küme deseni. `Id` ve `Name` taşır; lookup metodları ile değer/isim dönüşümü yapılır.

```csharp theme={null}
public abstract class Enumeration : IComparable
{
    public int Id { get; private set; }
    public string Name { get; private set; }
    protected Enumeration(int id, string name) { Id = id; Name = name; }

    public static IEnumerable<T> GetAll<T>() where T : Enumeration;
    public static T FromValue<T>(int id) where T : Enumeration;      // bulunamazsa DomainException
    public static T FromName<T>(string name) where T : Enumeration;  // bulunamazsa DomainException
    public static bool InRange<T>(int value) where T : Enumeration;  // geçerli mi?
}
```

Örnek tanım:

```csharp theme={null}
public class Role : Enumeration
{
    public static Role SuperAdmin = new(1, nameof(SuperAdmin));
    public static Role Admin      = new(2, nameof(Admin));
    public static Role Staff      = new(3, nameof(Staff));
    public static Role ReadOnly   = new(4, nameof(ReadOnly));
    public Role(int id, string name) : base(id, name) { }
}
```

<Tip>
  Aggregate'lerde sık görülen desen: DB'de `int StatusId` saklanır, domain tarafında ise hesaplanan bir property enumeration'a çevirir — `public UserStatus Status => UserStatus.FromValue<UserStatus>(StatusId);`
</Tip>

## GuidFactory

Kimlik üretimini tek noktada toplar. Hep **Version 7** (zaman sıralı) GUID üretir — bu, B-tree index'lerde daha iyi yerellik (locality) sağlar.

```csharp theme={null}
public static class GuidFactory
{
    public static Guid New() => Guid.CreateVersion7(DateTime.UtcNow);
}
```

Aggregate ctor'larında doğrudan kullanılır: `this.Id = GuidFactory.New();`

## DomainException

Domain kuralı ihlallerinde fırlatılan istisna tipi. API katmanında `ProblemDetails` ile **HTTP 400**'e map edilir.

```csharp theme={null}
public class DomainException : Exception
{
    public DomainException() { }
    public DomainException(string message) : base(message) { }
    public DomainException(string message, Exception innerException) : base(message, innerException) { }
}
```

## Check — guard yardımcısı

Argüman doğrulamasını okunabilir tek satırlara indirir; ihlalde `DomainException` (gerekirse iç `ArgumentException` ile) fırlatır.

```csharp theme={null}
Check.NotNull(plan, nameof(plan));
Check.NotNullOrEmpty(providerUserId, nameof(providerUserId));
Check.HasValue(id, nameof(id));          // default(T) değilse geçer
Check.NotEmpty(collection, nameof(collection));
Check.Positive(adet, nameof(adet));
```

Çocuk entity ctor'unda tipik kullanım:

```csharp theme={null}
public UserOtpChallenge(User user, Guid id, OtpCode code, OtpType type, DateTime expiryDate)
{
    Check.NotNull(user, nameof(user));
    Check.HasValue(id, nameof(id));
    Check.NotNull(code, nameof(code));
    // ...
}
```

## Audit, soft-delete ve tenant arayüzleri

Bu arayüzler `EntityBase<TId>` tarafından otomatik uygulanır; değerleri EF Core'daki tek `AuditInterceptor` doldurur (detay [Veri Katmanı](/data/dbcontext)).

| Arayüz             | Üyeler                                       | Amaç                                   |
| ------------------ | -------------------------------------------- | -------------------------------------- |
| `IAuditableEntity` | `CreatedAt/UpdatedAt`, `CreatedBy/UpdatedBy` | Yazma izi (kim, ne zaman).             |
| `ISoftDeletable`   | `DeletedAt`, `DeletedBy`, `IsDeleted()`      | Fiziksel silme yerine mantıksal silme. |
| `IMayHaveTenant`   | `Guid? OrganizationId`                       | Opsiyonel tenant aidiyeti.             |
| `ITenant`          | `Guid OrganizationId`                        | Zorunlu tenant aidiyeti.               |

```csharp theme={null}
public interface ISoftDeletable
{
    DateTime? DeletedAt { get; }
    Guid?     DeletedBy { get; }
    bool IsDeleted();
}
```

## Repository ve UnitOfWork arayüzleri

Domain, persistence'tan habersizdir; yalnızca arayüzleri tanımlar. Implementasyonlar Infrastructure katmanındadır (`EFRepository`, `CachedRepository`).

```csharp theme={null}
public interface IRepository<T> : IRepositoryBase<T> where T : class, IAggregateRoot
{
    IUnitOfWork UnitOfWork { get; }
}

public interface IReadRepository<T> : IReadRepositoryBase<T> where T : class, IEntityBase {}

public interface IUnitOfWork : IDisposable
{
    Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default);
}
```

<Warning>
  `IRepository<T>` yalnızca **`IAggregateRoot`** türlerini kabul eder. Çocuk entity'ler için doğrudan repository **yoktur** — onlara her zaman aggregate root üzerinden erişilir. Bu, tutarlılık sınırını derleme zamanında zorlar.
</Warning>

`IRepositoryBase<T>` ve `IReadRepositoryBase<T>`, **Ardalis.Specification** kütüphanesinden gelir; specification tabanlı sorgulama sağlar (`FirstOrDefaultAsync(new UserByEmailSpecification(email), ct)` gibi). Yazma akışında `SaveChanges` doğrudan değil, `UnitOfWork.SaveEntitiesAsync` üzerinden yapılır — bu metod kaydetme sonrası domain event'leri dispatch eder.

## Sonraki adımlar

<CardGroup cols={2}>
  <Card title="Aggregate'ler" icon="cubes" href="/domain/aggregates">
    Bu yapı taşlarının `User` aggregate'inde nasıl bir araya geldiği.
  </Card>

  <Card title="Value Object'ler" icon="gem" href="/domain/value-objects">
    `ValueObject` üzerine kurulu hazır VO kataloğu.
  </Card>

  <Card title="Domain Event'ler" icon="bolt" href="/domain/domain-events">
    `DomainEvent` + `INotification`'ın dispatch akışı.
  </Card>

  <Card title="Veri Katmanı" icon="database" href="/data/dbcontext">
    `AuditInterceptor`, soft-delete filter ve `SaveEntitiesAsync` detayları.
  </Card>
</CardGroup>
