1. Göç Mekanizmasının Genel Bakışı

1.1 Göçün Kavramı ve Rolü

Veritabanı göçü, veri modellerindeki değişiklikleri veritabanı yapısıyla senkronize etme sürecidir ve veri kalıcılığı için son derece önemlidir. Uygulama sürümü arttıkça, veri modeli genellikle değişikliklere uğrar, örneğin alan eklenmesi veya silinmesi, indekslerin değiştirilmesi gibi. Göç, geliştiricilerin bu değişiklikleri versiyonlanmış ve sistemli bir şekilde yönetmelerini sağlayarak, veritabanı yapısıyla veri modeli arasında tutarlılığı sağlar.

Modern web geliştirmede, göç mekanizması şu faydaları sağlar:

  1. Versiyon Kontrolü: Göç dosyaları, veritabanı yapısının değişim geçmişini takip edebilir, her versiyondaki değişiklikleri anlamak ve geri almak kolaylaşır.
  2. Otomatik Dağıtım: Göç mekanizması sayesinde, veritabanı dağıtımı ve güncellemeleri otomatik hale getirilebilir, manuel müdahalenin olasılığını ve hataların riskini azaltır.
  3. Ekip İşbirliği: Göç dosyaları, farklı geliştirme ortamlarında ekip üyelerinin senkronize veritabanı yapılarını kullanmalarını sağlar, işbirliğine olanak tanır.

1.2 ent Çerçevesinin Göç Özellikleri

ent çerçevesinin göç mekanizması ile entegrasyonu aşağıdaki özellikleri sunar:

  1. Bildirimsel Programlama: Geliştiriciler, sadece varlıkların Go temsiline odaklanmaları yeterlidir, ent çerçevesi varlıkları veritabanı tablolarına dönüştürme işlemini ele alır.
  2. Otomatik Göç: ent, DDL ifadeleri yazmadan veritabanı tablo yapılarını otomatik olarak oluşturabilir ve güncelleyebilir.
  3. Esnek Kontrol: ent, farklı göç gereksinimlerini desteklemek için çeşitli yapılandırma seçenekleri sunar, örneğin yabancı anahtar kısıtlamalarıyla veya kısa süreli benzersiz kimlik oluşturmayla.

2. Otomatik Göçe Giriş

2.1 Otomatik Göçün Temel Prensipleri

ent çerçevesinin otomatik göç özelliği, veritabanı yapısını oluşturmak için genellikle ent/schema dizininde bulunan şema tanım dosyalarına dayanır. Geliştiriciler varlıkları ve ilişkileri tanımladıktan sonra, ent mevcut yapıyı inceleyecek ve tablolar oluşturmak, sütun eklemek veya değiştirmek, indeksler oluşturmak vb. için ilgili işlemleri otomatik olarak oluşturacaktır.

Ayrıca, ent'in otomatik göç prensibi "ekleme modunda" çalışır: varsayılan olarak sadece yeni tablolar, yeni indeksler ekler veya tablolara sütun ekler, mevcut tabloları veya sütunları silmez. Bu tasarım, kazara veri kaybını önlemek için faydalıdır ve veritabanı yapısını ileriye dönük olarak kolayca genişletmek sağlar.

2.2 Otomatik Göçü Kullanma

ent otomatik göçünü kullanmak için temel adımlar aşağıdaki gibidir:

package main

import (
    "context"
    "log"
    "ent"
)

func main() {
    client, err := ent.Open("mysql", "root:pass@tcp(localhost:3306)/test")
    if err != nil {
        log.Fatalf("MySQL'e bağlanılamadı: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Otomatik göçü gerçekleştirerek veritabanı şemasını oluştur veya güncelle
    if err := client.Schema.Create(ctx); err != nil {
        log.Fatalf("Veritabanı şeması oluşturulamadı: %v", err)
    }
}

Yukarıdaki kodda, ent.Open veritabanıyla bağlantı kurmak ve bir istemci örneği döndürmekten sorumludur; client.Schema.Create ise gerçek otomatik göç işlemini yürütür.

3. Otomatik Göçün İleri Düzey Uygulamaları

3.1 Sütun ve İndeksleri Silme

Bazı durumlarda, artık gerekmeyen sütunları veya indeksleri veritabanı şemasından kaldırmamız gerekebilir. Bu durumda WithDropColumn ve WithDropIndex seçeneklerini kullanabiliriz. Örneğin:

// Sütun ve indeksleri silme seçenekleriyle göçü çalıştır
err = client.Schema.Create(
    ctx,
    migrate.WithDropIndex(true),
    migrate.WithDropColumn(true),
)

Bu kod parçacığı, otomatik göç sırasında sütunları ve indeksleri silme konfigürasyonunu etkinleştirir. ent, göçü gerçekleştirirken şema tanımında bulunmayan herhangi bir sütun veya indeksi silecektir.

3.2 Küresel Benzersiz Kimlik

SQL veritabanlarında, varsayılan olarak, birincil anahtarlar her tablo için 1'den başlar ve farklı varlık türleri aynı kimliği paylaşabilir. Bazı uygulama senaryolarında, GraphQL kullanılırken farklı varlık türlerinin kimlikleri için küresel benzersizlik sağlamak gerekebilir. ent'te bunu WithGlobalUniqueID seçeneği kullanarak yapılandırabiliriz:

// Her varlık için evrensel benzersiz kimliklerle göçü çalıştır
if err := client.Schema.Create(ctx, migrate.WithGlobalUniqueID(true)); err != nil {
    log.Fatalf("Veritabanı şeması oluşturulamadı: %v", err)
}

WithGlobalUniqueID seçeneği etkinleştirildikten sonra, ent her varlığa 2^32 aralıkta bir kimlik atayarak küresel benzersizliği sağlayacaktır.

3.3 Çevrimdışı Mod

Çevrimdışı mod, şema değişikliklerinin veritabanında doğrudan uygulanması yerine bir io.Writer'a yazılmasına olanak tanır. Bu, değişikliklerin etkili olmadan önce SQL komutlarını doğrulamak veya manuel olarak yürütmek için bir SQL betiği oluşturmak için faydalıdır. Örneğin:

// Göç (migration) değişikliklerini bir dosyaya aktar
f, err := os.Create("migrate.sql")
if err != nil {
    log.Fatalf("Göç dosyası oluşturulamadı: %v", err)
}
defer f.Close()
if err := client.Schema.WriteTo(ctx, f); err != nil {
    log.Fatalf("Veritabanı şema değişiklikleri yazdırılamadı: %v", err)
}

Bu kod, göç (migration) değişikliklerini "migrate.sql" adlı bir dosyaya yazacaktır. Pratikte, geliştiriciler doğrudan standart çıktıya yazdırabilir veya inceleme veya kayıt tutma amaçlı bir dosyaya yazabilir.

4. Yabancı Anahtar Desteği ve Özel Kancalar

4.1 Yabancı Anahtarları Etkinleştirme veya Devre Dışı Bırakma

Ent'te yabancı anahtarlar, varlıklar arasındaki ilişkileri (kenarlar) tanımlayarak uygulanır ve bu yabancı anahtar ilişkileri, veri bütünlüğünü ve tutarlılığını sağlamak üzere otomatik olarak veritabanı düzeyinde oluşturulur. Bununla birlikte, belirli durumlarda, performans optimizasyonu veya veritabanının yabancı anahtarı desteklememesi gibi durumlarda bunları devre dışı bırakmayı seçebilirsiniz.

Göçlerde yabancı anahtar kısıtlamalarını etkinleştirme veya devre dışı bırakma işlemini, WithForeignKeys yapılandırma seçeneği aracılığıyla kontrol edebilirsiniz:

// Yabancı anahtarları etkinleştir
err = client.Schema.Create(
    ctx,
    migrate.WithForeignKeys(true), 
)
if err != nil {
    log.Fatalf("Yabancı anahtarlarla şema kaynakları oluşturulurken hata oluştu: %v", err)
}

// Yabancı anahtarları devre dışı bırak
err = client.Schema.Create(
    ctx,
    migrate.WithForeignKeys(false), 
)
if err != nil {
    log.Fatalf("Yabancı anahtarlar olmadan şema kaynakları oluşturulurken hata oluştu: %v", err)
}

Bu yapılandırma seçeneği, Schema.Create çağrılırken geçirilmelidir ve belirtilen değere göre oluşturulan DDL'de yabancı anahtar kısıtlamalarını içerip içermeme durumunu belirler.

4.2 Göç Kancalarının Uygulanması

Göç kancaları, göç yürütme işleminin farklı aşamalarında eklenip yürütülebilen özel mantıksal işlemlerdir. Veritabanı göçünden önce/sonra belirli mantık işlemlerini gerçekleştirmek için oldukça faydalıdır, örneğin göç sonuçlarını doğrulama ve veri ön yükleme gibi.

İşte özel göç kancalarının nasıl uygulanacağına dair bir örnek:

func customHook(next schema.Creator) schema.Creator {
    return schema.CreateFunc(func(ctx context.Context, tables ...*schema.Table) error {
        // Göçten önce yürütülecek özel kod
        // Örneğin, günlükleme, belirli ön koşulların kontrolü vb.
        log.Println("Göçten önce özel mantık")
        
        // Sonraki kancayı veya varsayılan göç mantığını çağır
        err := next.Create(ctx, tables...)
        if err != nil {
            return err
        }
        
        // Göçten sonra yürütülecek özel kod
        // Örneğin, temizlik, veri göçü, güvenlik kontrolleri vb.
        log.Println("Göçten sonra özel mantık")
        return nil
    })
}

// Göçte özel kancaların kullanılması
err := client.Schema.Create(
    ctx,
    schema.WithHooks(customHook),
)
if err != nil {
    log.Fatalf("Özel göç kancalarının uygulanmasında hata oluştu: %v", err)
}

Kancalar, karmaşık göçler için güçlü ve vazgeçilmez araçlardır ve ihtiyaç duyulduğunda doğrudan veritabanı göç davranışını kontrol etme yeteneği sağlar.

5. Sürümlenmiş Göçler

5.1 Sürümlenmiş Göçlere Giriş

Sürümlenmiş göç, veritabanı göçlerini yönetmek için bir desen olup geliştiricilere veritabanı yapısındaki değişiklikleri belirli bir dizi veritabanı değişiklik komutlarını içeren birden çok sürüme bölmelerine olanak tanır. Otomatik Göç'e kıyasla, sürümlenmiş göç daha ince kontrol sağlayarak veritabanı yapısı değişikliklerinin izlenebilirliğini ve geri alınabilirliğini garanti altına alır.

Sürümlenmiş göçün temel avantajı, ileri ve geri göç (yükseltme veya geri alma) desteğidir, bu da geliştiricilere ihtiyaca göre belirli değişiklikleri uygulamalarına, geri almalarına veya atlamalarına olanak tanır. Bir ekip içinde işbirliği yapıldığında, sürümlenmiş göç, her üyenin aynı veritabanı yapısı üzerinde çalışmasını sağlayarak tutarsızlık kaynaklı sorunları azaltır.

Otomatik göç genellikle geri alınamaz, varlık modellerinin en son durumuna eşleşen SQL ifadeleri oluşturur ve yürütür, genellikle geliştirme aşamalarında veya küçük projelerde kullanılır.

5.2 Sürümlenmiş Göçlerin Kullanımı

1. Atlas Aracının Kurulumu

Versiyonlanmış göçlerden önce, sisteminize Atlas aracını kurmanız gerekmektedir. Atlas, birden fazla veritabanı sistemini destekleyen, veritabanı şema değişikliklerini yönetmek için güçlü özellikler sunan bir göç aracıdır.

macOS + Linux

curl -sSf https://atlasgo.sh | sh

Homebrew

brew install ariga/tap/atlas

Docker

docker pull arigaio/atlas
docker run --rm arigaio/atlas --help

Windows

https://release.ariga.io/atlas/atlas-windows-amd64-latest.exe

2. Mevcut Varlık Tanımlarına Göre Göç Dosyalarının Oluşturulması

atlas migrate diff migration_name \
  --dir "file://ent/migrate/migrations" \
  --to "ent://ent/schema" \
  --dev-url "docker://mysql/8/ent"

3. Uygulama Göç Dosyaları

Göç dosyaları oluşturulduktan sonra, bunlar geliştirme, test veya üretim ortamlarına uygulanabilir. Genellikle, bu göç dosyalarını önce bir geliştirme veya test veritabanına uygular ve beklediğiniz gibi çalıştıklarından emin olursunuz. Ardından, aynı göç adımları üretim ortamında gerçekleştirilir.

atlas migrate apply \
  --dir "file://ent/migrate/migrations" \
  --url "mysql://root:pass@localhost:3306/example"

Göç dosyalarını uygulamak için atlas migrate apply komutunu kullanarak, göç dosyası dizinini belirleyin (--dir) ve hedef veritabanının URL'sini (--url) belirtin.