asyncfunctionrenderPage(){const data =awaitgetPageDatas();return data;}
Bileşenler hazırlanıyor...
Not içeriği yükleniyor...
// Markdown dosyası okunuyor
// İçerik işleniyor
// Syntax highlighting hazırlanıyor
DDD Entity Nedir? | Domain Modeling ve Identity Yönetimi | Kubilay Bozak
DDD Entity Nedir ?
2025-06-06
25 min
DDD Entity kavramı, identity ve lifecycle yönetimi. Domain modellemede Entity'lerin rolü ve implementasyon best practices.
DDD
Entity
Domain
Modeling
Identity
🧱 Entity Nedir?
DDD’de Entity, kimliği (identity) olan, yaşam döngüsü boyunca kimliğiyle takip edilen nesnedir. Yani bir varlığı temsil eder ve bu varlığın kendine has bir kimliği vardır. Kimlik, o varlığın diğer varlıklardan ayrılmasını sağlar.
Özellikleri:
Kimlik (Identity): Her Entity’nin benzersiz bir kimliği vardır.
Davranış: Entity sadece veri değil, aynı zamanda iş kurallarını kapsar.
Yaşam Döngüsü: Entity, oluşturulma, değiştirilme, silinme gibi süreçlere sahiptir ve bu süreçler boyunca kimliği sabittir.
Mutabilite: Entity’nin durumu değişebilir ancak kimliği değişmez.
Örnek Domain Entity:
bash
public 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;}}
Bu örnekte Customer entity’si sadece verileri değil, aynı zamanda müşteriyle ilgili davranışları da kapsıyor.
Burada son kez bir hatırlatma geçmemde fayda var diye düşüyorum ,
EF Core tarafında 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):
Bellek (memory) içinde yaşarken bile o nesneyi tanımlamak için kullanılır. Henüz veritabanıyla hiçbir bağlantı kurmamış olsa bile, bir 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.
🔍 Özetle şöyle diyebiliriz:
✅ EF Core Entity: 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.
Örnek verelim
🎯 EF Core Entity Yaklaşımı (Persistence Odaklı)
bash
public 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).
Veritabanı odaklı çalışılır.
Order henüz veritabanına eklenmediyse Id null/0 olabilir.
Kimlik, sistem için değil, veritabanı için önemlidir.
🧠 DDD Entity Yaklaşımı (Domain Odaklı)
bash
csharp
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.
Veritabanıyla hiçbir ilgisi olmasa bile, bu Order sistemde tanınabilir.
Kimlik, iş mantığına göre belirlenir ve tüm sistem boyunca aynı kalır.
📌 Ne Kazanırız?
DDD yaklaşımı sayesinde:
Order nesnesi, henüz veritabanına kaydedilmeden bile anlamlı hale gelir.
Test edilebilirlik artar (in-memory nesnelerle çalışmak mümkün).
Domain kuralları veriden ayrılır.
Şimdi Entity kavramını anladığımızı düşünüyorum.
Bir sonraki konumuz olan Value Objects kavramına geçmeden önce sizler ile bir konuda beraber fikir yürüterek özellikle DDD’de 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.
bash
namespace 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;}}}
bash
namespace 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;}}}
Gördüğünüz 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:
Peki biz projemizde bir adet 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.
bash
public 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));}}
Sizce buradaki çıktılar sırasıyla ne olur ?
A-) True True
B-) True False
Kendine şöyle bir soru sormanı istiyorum.
Burada iki farklı nesne oluşturuyorsun, ancak aynı ID’yi taşıyorlar. Bunlar gerçekten aynı varlık mıdır? DDD açısından evet, çünkü kimlik (ID) aynıdır.
Ama C# dilinde 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.
İşte burada Value Object kısmına geçmeden önce bilmemiz gereken bir kavram var. Equality Check
🎯 Eşitlik Kontrolü (Equality Check) Nedir ve Neden Önemlidir?
Bu terim, genellikle Entity'nin eşitlik karşılaştırması (equality check) için bir kontrol sistemini ifade eder. Yani:
İki Entity'nin aynı mı olduğunu nasıl anlarız?
Aynı ID’ye sahiplerse aynı Entity midir?
Yoksa başka şeylere de bakmalı mıyız?
Entity’ler, kimlikleri (ID) ile tanımlanır. Yani bir Entity’nin eşitliği, içindeki tüm alanların aynı olmasıyla değil, kimliklerinin aynı olmasıyla belirlenir.
Bunun anlamı:
İki farklı nesne örneği olabilir (bellekte farklı adreslerde duruyorlar) ama aynı Id değerine sahiplerse, bu iki Entity “aynıdır”.
Tersine, tüm alanları aynı olsa bile Id farklıysa, bu iki Entity farklıdır.
🎯 Neden standart referans karşılaştırması yeterli değil?
C#’ta iki nesne == 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.
Ancak DDD’de, farklı bellek adreslerinde olsalar bile, aynı kimliğe sahip Entity’lerin eşit kabul edilmesi gerekir.
Bu yüzden Entity sınıflarında eşitlik operatörleri ve Equals() metodu override edilerek, kimlik bazlı karşılaştırma yapılmalıdır.
bash
public 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 /*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. */
/*
Burada ufak bir dipnot belirtmek istiyorum.
equals methodundan daha biraz daha advance bir method.
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.
*/
Entity’ler üzerinden ilerlerken, kimliği olan ve yaşam döngüsü boyunca izlenen varlıkları tanımladık. Ancak, yazılım sistemimizde her şey bir kimlik taşımaz. Bazı kavramlar vardır ki, onları temsil eden veriler değiştiğinde, artık farklı bir şeyi temsil ederler — tıpkı bir adres, bir para birimi ya da bir koordinat gibi.
Bu tür kavramları modellemek için DDD bize başka bir yapı sunar: Value Object.
Şimdi gelin, Value Object nedir, Entity’den farkı nedir ve neden bu kadar önemli, birlikte bir sonraki makalemde inceleyelim.
(
obj is null
)
{
return
false
;
}
if
(
obj is not Entity entity
)
{
return
false
;
}
if
(
obj.GetType
(
)
==
GetType
(
))
{
return
false
;
}
return
entity.Id
==
Id
;
}
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 kullandığımız IEquatable
<
Entity
>
arayüzü ile kendi oluşturduğumuz
Fark Şu
:
- Kendi override ettiğimiz method Unboxing yapıyor yani basitçe objeyi tipe çeviriyor,