Not içeriği yükleniyor...
// Markdown dosyası okunuyor
// İçerik işleniyor
// Syntax highlighting hazırlanıyor
// Markdown dosyası okunuyor
// İçerik işleniyor
// Syntax highlighting hazırlanıyor
DDD Entity kavramı, identity ve lifecycle yönetimi. Domain modellemede Entity'lerin rolü ve implementasyon best practices.
bashpublic class Customer { public Guid Id { get; private set; } public string Name { get; private set; } public string Email { get; private set; } public bool IsActive { get; private set; } public Customer(Guid id, string name, string email) { Id = id; Name = name; Email = email; IsActive = true; } public void ChangeEmail(string newEmail) { // Basit bir validasyon örneği if (string.IsNullOrWhiteSpace(newEmail) || !newEmail.Contains("@")) throw new ArgumentException("Geçerli bir email adresi giriniz."); Email = newEmail; } public void Deactivate() { IsActive = false; } }
Customer entity’si sadece verileri değil, aynı zamanda müşteriyle ilgili davranışları da kapsıyor.Id genellikle veritabanındaki benzersiz kimliği temsil eder. Yani: Veri tabanına kayıt eklenir, EF Core o kaydın Id alanını otomatik üretir (örneğin bir int veya GUID), Biz bu kimliği, veriyi sorgularken kullanırız. Bu yaklaşım tamamen persist (kalıcı) veriye odaklıdır.
DDD tarafında ise Entity'nin kimliği (ID):Order nesnesi Id değerine sahiptir ve bu sayede uygulama içinde tanımlıdır. Yani burada kimlik, sadece veri saklamak için değil, domain mantığında varlıkları ayırt etmek içindir.Id, veritabanında o satırı benzersiz yapan şeydir. (Persistence odaklı)
✅ DDD Entity: Id, sistem içindeki varlıkları birbirinden ayıran, işsel kimliktir. Veritabanına bağımlı değildir. (Domain odaklı)
🧠 Hatta bazı DDD uygulamalarında, kimlik veritabanı tarafından değil, uygulama tarafından (örneğin Guid.NewGuid() ile) belirlenir. Çünkü kimlik domain’e aittir, veritabanına değil.bashpublic class Order { public int Id { get; set; } // Veritabanı tarafından atanır (Identity) public DateTime CreatedAt { get; set; } public decimal TotalAmount { get; set; } }
Id genellikle veritabanı tarafından üretilir (IDENTITY veya SEQUENCE).Id null/0 olabilir.bashcsharp KopyalaDüzenle public class Order { public Guid Id { get; private set; } // Uygulama tarafından atanır public DateTime CreatedAt { get; private set; } public decimal TotalAmount { get; private set; } public Order(decimal totalAmount) { Id = Guid.NewGuid(); // Kimlik veritabanı değil, uygulama tarafından atanır CreatedAt = DateTime.UtcNow; TotalAmount = totalAmount; } public void UpdateTotal(decimal newTotal) { if (newTotal < 0) throw new InvalidOperationException("Tutar negatif olamaz."); TotalAmount = newTotal; } }
Id, uygulama içinde oluşturulur ve domain kimliği olarak kullanılır.Order sistemde tanınabilir.Value Object kavramını anlamanın en kritik noktalarından biri, eşitlik (equality) mantığının beraber farkına varalım istiyorum.
Şimdi örnek olarak bir tane Category classımız olsun.bashnamespace DomainDrivenDesign.Domain.Categories { public sealed class Category: Entity { public Category(Guid CreatedTimeAssignedId) : base(CreatedTimeAssignedId) { } public string Name { get; set; } public ICollection<Product> Products { get; set; } } }
bashnamespace DomainDrivenDesign.Domain.Abstractions { //Sadece Kalıtım alınsın istiyoruz kendi başına bir oluşum olmasın //Bu yüzden abstract olarak tanımlıyoruz public abstract class Entity { public Guid Id { get; init; } //Inıt property olarak tanımladık ki sadece constructor'da atama yapılabilsin ve değiştirilemesin protected Entity(Guid CreatedTimeAssignedId) { this.Id = CreatedTimeAssignedId; } } }
Category sınıfı, senin daha önce tanımladığın Entity sınıfından kalıtım (inheritance) alıyor ve bu sayede bir DDD Entity'si oluyor:Guid Id değeri oluştursak daha sonra bu oluşturduğumuz id değeri ile 2 tane class tanımlasak şimdiye kadar konuştuğumuz DDD mantığına göre iki Entity'nin aynı mıdır ?
Şöyle inceleyelim isterseniz.bashpublic class ExampleClass : Entity { public ExampleClass(Guid CreatedTimeAssignedId) : base(CreatedTimeAssignedId) { } } public class TestRunner { public void main() { Guid oneTimeCreatedGuid = Guid.NewGuid(); ExampleClass example1 = new(oneTimeCreatedGuid); ExampleClass example2 = new(oneTimeCreatedGuid); Console.WriteLine(example1.Id == example2.Id); Console.WriteLine(example1.Equals(example2)); } }
example1 == example2 sonucu false çıkar çünkü referans karşılaştırması yapılır.
Peki ben şimdi neden böyle bir soru sordum ?
Başından beri Entity kavramını anlatırken ne diyorduk?
Benzersiz bir kimlik değerine sahip olan her bir yapı, sistem içerisinde kimliğiyle tanımlanır ve takip edilir — bu nedenle referansları farklı olsa bile, kimlikleri aynıysa aynı Entity olarak kabul edilirler.Id değerine sahiplerse, bu iki Entity “aynıdır”.Id farklıysa, bu iki Entity farklıdır.== ile karşılaştırıldığında, varsayılan olarak referans eşitliği kontrol edilir. Yani bellekte aynı nesne olup olmadığına bakılır.Equals() metodu override edilerek, kimlik bazlı karşılaştırma yapılmalıdır.
bashpublic abstract class Entity : IEquatable<Entity> { public Guid Id { get; init; } protected Entity(Guid CreatedTimeAssignedId) { this.Id = CreatedTimeAssignedId; } /* Equals metodunu override ettik çünkü Entity sınıfından türetilen sınıfların karşılaştırılmasını istiyoruz */ public override bool Equals(object? obj) { if (obj is null) { return false; } if (obj is not Entity entity) { return false; } if (obj.GetType() == GetType()) { return false; } return entity.Id == Id; } /*GetHashCode metodunu override ettik çünkü Entity sınıfından türetilen Array veya List gibi koleksiyonlarda kullanılacaksa, bu metodun da doğru çalışması gerekiyor. */ public override int GetHashCode() { return Id.GetHashCode(); } // IEquatable<Entity> arayüzünü implement ettik çünkü Entity sınıfından türetilen sınıfların karşılaştırılmasını istiyoruz public bool Equals(Entity? other) { if (other is null) { return false; } if (other is not Entity entity) { return false; } if (other.GetType() != GetType()) { return false; } return entity.Id == Id; } /* Burada ufak bir dipnot belirtmek istiyorum. Burada kullandığımız IEquatable<Entity> arayüzü ile kendi oluşturduğumuz equals methodundan daha biraz daha advance bir method. Fark Şu : - Kendi override ettiğimiz method Unboxing yapıyor yani basitçe objeyi tipe çeviriyor, Tipe göre kontrol etme işlemi yapıyor biraz daha kötü performans sağlıyor. - İmplement ettiğimiz method ise Unboxing yapmıyor. */ }