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

# Migrations

> EF Core migration üretme/uygulama, DesignTime factory, MigrationService AutoDeploy

Proje code-first EF Core kullanır. Migration'lar `Infrastructure.EFCore/Migrations/` altında durur; runtime'da `MigrationService` (bir `IHostedService`) tarafından otomatik uygulanır.

## Migration üretme

`dotnet ef` komutları DbContext'in bulunduğu projede (`Infrastructure.EFCore`) çalışır, ama configuration'ı (connection string) okuyabilmek için startup projesi (`API`) gösterilir:

```bash theme={null}
dotnet ef migrations add <MigrationAdi> \
  --project    src/DiyanetCleanArchitecture.Infrastructure.EFCore \
  --startup-project src/DiyanetCleanArchitecture.API
```

Migration'ı geri almak (henüz DB'ye uygulanmadıysa):

```bash theme={null}
dotnet ef migrations remove \
  --project    src/DiyanetCleanArchitecture.Infrastructure.EFCore \
  --startup-project src/DiyanetCleanArchitecture.API
```

İdempotent SQL script üretmek (DBA'nın manuel uyguladığı senaryo için):

```bash theme={null}
dotnet ef migrations script --idempotent \
  --project    src/DiyanetCleanArchitecture.Infrastructure.EFCore \
  --startup-project src/DiyanetCleanArchitecture.API \
  --output     migration.sql
```

## DesignTimeDbContextFactory

`dotnet ef` design-time'da çalışırken DI container yoktur — runtime'daki `IMediator`, `IEventBus`, `IAuditUserContext` mevcut değildir. Bu yüzden `DiyanetCleanArchitectureDesignTimeDbContextFactory`, no-op dummy'lerle DbContext kurar:

```csharp theme={null}
public class DiyanetCleanArchitectureDesignTimeDbContextFactory
    : DesignTimeDbContextFactoryBase<DiyanetCleanArchitectureDbContext>
{
    protected override DiyanetCleanArchitectureDbContext CreateNewInstance(
        DbContextOptions<DiyanetCleanArchitectureDbContext> options)
        => new(options, new NoMediator(), new NoEventBus(),
               new AuditInterceptor(new NoAuditUserContext()));
}
```

`NoAuditUserContext` her zaman `SystemActor.Id` döner — design-time bir şekilde `SaveChanges` tetiklerse bile audit kolonlarına sağlam bir değer yazılır, exception fırlamaz.

Base sınıf (`DesignTimeDbContextFactoryBase`) connection string'i API projesinin `appsettings`'inden okur:

```csharp theme={null}
private const string ConnectionStringName = "DiyanetCleanArchitectureDatabaseConnection";
private readonly string BasePath =
    Path.Combine(Directory.GetCurrentDirectory(), "../DiyanetCleanArchitecture.API");

// appsettings.json + appsettings.{ASPNETCORE_ENVIRONMENT}.json + env değişkenleri
optionsBuilder.UseNpgsql(connectionString)
              .UseSnakeCaseNamingConvention();
```

<Note>
  Komutu `Infrastructure.EFCore` klasöründen çalıştırın; `BasePath` `../DiyanetCleanArchitecture.API`'ye göre relatiftir. Connection string boşsa factory anlamlı bir hata fırlatır.
</Note>

## Migration uygulama — MigrationService

Runtime'da migration `MigrationService : IHostedService` ile uygulanır. Sadece config'te `AutoDeploy=true` ise çalışır:

```csharp theme={null}
public async Task StartAsync(CancellationToken cancellationToken)
{
    if (_options.CurrentValue.AutoDeploy == false)
    {
        _logger.LogInformation("📢 Migration servisi kapalı.");
        return;
    }

    using var scope = _serviceProvider.CreateScope();
    var dbContext = scope.ServiceProvider.GetRequiredService<DiyanetCleanArchitectureDbContext>();

    int retryCount = 0;
    while (retryCount < MaxRetryCount && !cancellationToken.IsCancellationRequested) // MaxRetryCount = 3
    {
        try
        {
            if (!await dbContext.Database.CanConnectAsync(cancellationToken))
                _logger.LogWarning("🆕 Veritabanı bulunamadı. İlk kurulum başlatılıyor...");

            await dbContext.Database.MigrateAsync(cancellationToken);
            return;
        }
        catch (Exception ex)
        {
            retryCount++;
            if (retryCount >= MaxRetryCount) throw;
            await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
        }
    }
}
```

Özellikler:

* **AutoDeploy guard:** Kapalıysa hiçbir şey yapmaz.
* **Retry:** En fazla 3 deneme, her başarısızlıkta 5 sn bekler. 3. denemede de patlarsa exception yeniden fırlatılır (uygulama başlamaz).
* **`Database.MigrateAsync`:** Bekleyen tüm migration'ları uygular; DB yoksa oluşturur.

Konfigürasyon (`appsettings.json`):

```json theme={null}
{
  "Services": {
    "Migration": {
      "AutoDeploy": true
    }
  }
}
```

`MigrationService`, `AddInfrastructureEFCore` içinde `DevDataSeeder` ve `DistrictSeeder` ile birlikte hosted service olarak kaydedilir.

## Docker'da migration

Compose senaryolarında API container'ı `AutoDeploy=true` ile açılır; postgres servisi henüz hazır değilse `MigrationService`'in retry'ı (3×5 sn) köprü görevi görür. Tek instance'ta bu yeterlidir.

<Warning>
  Çok-instance (replica) prod'da her replica `MigrateAsync` çalıştırırsa yarış oluşabilir. Bu durumda ya tek bir "migration job" container'ı `AutoDeploy=true` ile açılır, diğer API replica'larında `AutoDeploy=false` bırakılır; ya da migration CI/CD adımında idempotent SQL script ile uygulanır.
</Warning>

## Migration listesi

`Migrations/` klasöründeki migration'lar (kronolojik — uygulama sırası budur):

| Migration                                      | İçerik                                             |
| ---------------------------------------------- | -------------------------------------------------- |
| `20260506111334_initial`                       | Temel şema (User, Role, Organization, Location...) |
| `20260508073844_RolePermissionFix`             | RolePermission düzeltmesi                          |
| `20260513115344_userchange`                    | User aggregate değişiklikleri                      |
| `20260514190633_ContentModules`                | Faq, Announcement, Center, SiteSettings            |
| `20260518115056_CitizenAggregate`              | Citizen (vatandaş portalı) aggregate'i             |
| `20260518202850_RolePermissionsRevamp`         | RolePermission Guid PK + partial unique index      |
| `20260519130903_OutboxInboxMessagingSchema`    | MassTransit `messaging` şeması (outbox/inbox)      |
| `20260522100332_BasvuruAggregates`             | Bağış & Etkinlik başvuru + plan aggregate'leri     |
| `20260523185247_ProfileImageAndLegalDocuments` | ProfileImage + LegalDocument/Version               |
| `20260524132444_AddAdminNotifications`         | AdminNotification + per-user okuma kaydı           |
| `20260525072317_AddSupportTickets`             | SupportTicket aggregate'i                          |
| `20260525073703_AddSupportTicketPermissions`   | Destek talebi izinleri (seed)                      |
| `20260531192628_AddAnnouncementCoverImage`     | Duyuru kapak görseli                               |

Model değişikliklerinin tutarlılığı `Migrations/DiyanetCleanArchitectureDbContextModelSnapshot.cs` ile takip edilir — bu dosya elle düzenlenmez, EF tarafından yönetilir.

## İlgili

<CardGroup cols={2}>
  <Card title="DbContext & Repository" href="/data/dbcontext">
    DbSet'ler, OnModelCreating, EntityTypeConfiguration örnekleri.
  </Card>

  <Card title="Seed Data" href="/data/seed-data">
    Migration `HasData` seed'leri ve hosted seeder'lar.
  </Card>

  <Card title="Soft Delete" href="/data/soft-delete">
    Partial unique index (`deleted_at IS NULL`) migration'larda nasıl üretilir.
  </Card>

  <Card title="Data Genel Bakış" href="/data/overview">
    Katman rolü ve klasör yapısı.
  </Card>
</CardGroup>
