internal constructor’ı, oluşturmanın kontrollü olmasını sağlar (bkz. Aggregate’ler). Ama bir kullanıcı oluşturmadan önce “bu e-posta zaten var mı?” gibi kontroller yapmak gerekir — bu kontroller tek bir aggregate örneğinin sorumluluğu değildir. İşte burada factory ve domain service devreye girer.
Factory
Karmaşık oluşturma mantığını kapsüller: uniqueness kontrolü, dış IdP eşleştirme, doğru ctor seçimi. Sonuç: geçerli bir aggregate.
Domain Service
Tek bir aggregate’e doğal olarak ait olmayan iş kurallarını barındırır (genelde birden fazla aggregate’i ya da repository’yi ilgilendirir).
IUserFactory — kullanıcı oluşturma fabrikası
User’ın ctor’u internal olduğu için, dışarıdan oluşturmanın tek meşru yolu factory’dir. Arayüz, tüm kayıt senaryolarını kapsar:
CreateWith...— sıfırdan kayıt (telefon veya e-posta ile). Uniqueness başarısızsaDomainExceptionfırlatır.GetOrCreateFrom...— dış IdP (Google/Meta/Keycloak) login akışı. Mevcut kullanıcı varsa bağlar, yoksa oluşturur.
Uniqueness kontrolü ile oluşturma
Factory,
User’ı oluşturmadan önce IUserUniquenessChecker’a danışır. internal ctor sayesinde kimse bu kontrolü atlayarak User oluşturamaz — geçersiz durum domain dışına çıkamaz.Dış IdP akışı — GetOrCreateFromExternalAsync
Google ve Meta ortak bir özel yardımcıyı kullanır. Eşleştirme önceliği üç adımdır:1
Provider + sub eşleşmesi
(provider, externalId) ikilisi daha önce bağlanmışsa, o kullanıcı bulunur ve linkLogin ile display name tazelenir.2
E-posta eşleşmesi
Aynı e-posta başka bir IdP’den gelmiş olabilir. Varsa mevcut kullanıcıya bu provider da eklenir (multi-provider) ve e-posta doğrulanmış sayılır.
3
Yeni kullanıcı
Hiçbir eşleşme yoksa yeni
User oluşturulur, IdP bağlanır, e-posta varsa VerifyEmail() çağrılır.linkLogin, provider’a göre değişen bağlama davranışını parametre olarak alır:
Domain Service: IUserUniquenessChecker
Bir kullanıcının e-posta/telefon/dış kimlik açısından benzersiz olup olmadığı, tek birUser örneğinin bilemeyeceği bir kuraldır — tüm kullanıcı kümesine bakmak gerekir. Bu yüzden bir domain service’tir.
UniquenessResult, factory’nin kullanıcıya anlamlı mesaj verebilmesini sağlar: “zaten var, giriş yap” ile “var ama doğrulanmamış, doğrulamayı tamamla” farklı yönlendirmeler gerektirir.
Domain Service: UserRegistrationService
Daha basit bir kayıt yolu sunan, repository üzerinden doğrudan uniqueness kontrolü yapıpUser döndüren bir domain service:
Ne zaman factory, ne zaman domain service?
Factory kullan — oluşturma karmaşıksa
Factory kullan — oluşturma karmaşıksa
Bir aggregate’i oluşturmak ek mantık gerektiriyorsa (uniqueness, dış IdP eşleştirme, hangi ctor/overload seçileceği, başlangıç event’leri) factory uygundur. Çıktısı her zaman geçerli bir aggregate’tir.
Domain service kullan — kural tek aggregate'e ait değilse
Domain service kullan — kural tek aggregate'e ait değilse
İş kuralı birden fazla aggregate’i, tüm bir koleksiyonu (uniqueness) veya repository sorgusunu ilgilendiriyorsa domain service’e koyun. Stateless’tır; durumu aggregate’lerde bırakır.
İkisi birlikte
İkisi birlikte
Pratikte
UserFactory, oluşturma orkestrasyonunu yapar ve kural değerlendirmesi için IUserUniquenessChecker domain service’ine delege eder. Sorumluluklar net ayrılır: factory “nasıl oluşturulur”, service “izin var mı”.Sonraki adımlar
Aggregate'ler
internal ctor ve User davranış metotları.Domain Event'ler
Oluşturmada yayılan
UserCreatedDomainEvent.SharedKernel
IRepository, IReadRepository ve specification altyapısı.Domain'e genel bakış
Katmanın rolü ve klasör yapısı.