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
Value Objects kavramı, immutability ve equality semantics. Entity'lerden farkları ve domain modeling'de kullanım senaryoları.
Value Object, kimliği olmayan ve tamamen değeriyle tanımlanan nesnelerdir. Yani bir Value Object’in kimliği yoktur; içeriği aynıysa, o nesne eşittir. Genellikle Immutable yani değiştirilemez olması amaçlanır.
Özellik | Açıklama |
---|---|
🆔 Kimlik yoktur | Herhangi bir Id alanı içermez. Kimliksizdir. |
📦 Değere göre eşitlik | İçerdiği tüm alanlar aynıysa, iki VO eşittir. |
♻️ Immutable’dır | Değeri değiştirilemez; yeni bir VO oluşturulur. |
🎯 Anlamlı bir kavramı temsil eder | Örneğin: Adres, Para, Koordinat, Tam Ad, Email gibi. |
bashpublic sealed class User: Entity { public User(Guid CreatedTimeAssignedId) : base(CreatedTimeAssignedId) { } public string Name { get; set; } public string Email { get; set; } public string Password { get; set; } public string Country { get; set; } public string City { get; set; } public string Street { get; set; } public string FullAddress { get; set; } public string PostalCode { get; set; } }
string
olarak tutulur. Ancak bu yaklaşımda:"123"
hem postal kod, hem ad, hem şifre olabilir.`bashpublic sealed record AddressRecord( string Country , string City , string Street , string FullAddress , string PostalCode);
bashpublic sealed record AddressRecord( string Country, string City, string Street, string FullAddress, string PostalCode) { public AddressRecord { if (string.IsNullOrWhiteSpace(Country)) throw new ArgumentException("Country boş olamaz."); if (string.IsNullOrWhiteSpace(City)) throw new ArgumentException("City boş olamaz."); // Diğer kurallar... } }
bashpublic sealed class Address { public string Country { get; } public string City { get; } public string Street { get; } public string FullAddress { get; } public string PostalCode { get; } public Address(string country, string city, string street, string fullAddress, string postalCode) { Country = country; City = city; Street = street; FullAddress = fullAddress; PostalCode = postalCode; } public override bool Equals(object obj) { if (obj is not Address other) return false; return Street == other.Street && City == other.City && Country == other.Country; } public override int GetHashCode() { return HashCode.Combine(Street, City, Country); } }
public string Value { get; }
yerine get; private set;
mi, get; init;
mi, başka bir erişim belirleyici mi kullanmalıyım?
Bu sorunun cevabı şuna bağlı:
Value Object’in bu property'si nereden ve ne zaman değiştirilecek? public string Value { get; }
public string Value { get; init; }
public string Value { get; }
public string Value { get; init; }
object initializer
veya constructor
) atanabilir.public Email Email { get; init; } // sadece ilk atamada kullanılabilir
record
tipleriyle daha sık kullanılır.public string Value { get; private set; }
public string Value { get; private set; } // class içinden set edilebilir ❌
protected set
, internal set
gibi diğer erişimlerprotected set
: Alt sınıflar değiştirebilir.internal set
: Aynı assembly içinden değiştirilebilir.Kullanım Durumu | Tercih |
---|---|
Value Object | public string Value { get; } veya init |
Entity içinde, EF Core set etsin istiyorsan | get; private set; (Entity'de olabilir, VO’da dikkatli kullanılır) |
Tam immutable yapı istiyorsan | get; |
bashpublic sealed record Email { public string Value { get; } public Email(string value) { if (string.IsNullOrWhiteSpace(value)) { throw new ArgumentException("Email cannot be null or empty", nameof(value)); } Value = value; if(string.Compare(value, "example.com", StringComparison.OrdinalIgnoreCase) == 0) { throw new ArgumentException("Email cannot be example.com", nameof(value)); } if(!value.Contains("@")) { throw new ArgumentException("Email must contain '@'", nameof(value)); } } }
Value Object
sınıfları oluşturduk.
string Email
ne demek? Herhangi bir metin olabilir. Ama Email
adında bir Value Object
sınıfı varsa, artık o sadece bir metin değil, geçerli bir e-posta adresini temsil eden anlamlı bir kavram olur.
✅ Artık Email
tipindeki bir değişken sadece "abc"
olamaz. Çünkü oluşturulurken validasyon yapılır. Bu da domain kurallarını korumanı sağlar.
bashpublic sealed class User : Entity { public User(Guid CreatedTimeAssignedId) : base(CreatedTimeAssignedId) { } public Name Name { get; set; } public Password Password { get; set; } public Email Email { get; set; } public AddressRecord Address { get; set; } // Address sınıfını burada kullanıyoruz }
bashUser kubilay = new(Guid.NewGuid()); kubilay.Email = new(""); //Erişip değişiklik yapabiliyorum. Console.Write(kubilay.Name);
public set
kullanırsak, dışarıdan istediğimiz gibi doğrudan ve kontrolsüz şekilde property değerleri değiştirilebiliriz.
Bunu engellememiz için ve tutarlılığı sağlayabilmemiz için bizim ilk önce yapmamız gereken şey User nesnesi oluşurken bu değerleri atamak eğer User nesnesinde daha sonra bu nesneleri yada değerleri değiştirmek istiyorsak bunu User Entity'si içinde yapmamız gerekir.
İşte bunu sağlamak için public set
yerine private set
kullanmamız gerekir.bashpublic sealed class User : Entity { public User(Guid CreatedTimeAssignedId) : base(CreatedTimeAssignedId) { } public Name Name { get; private set; } public Password Password { get; private set; } public Email Email { get; private set; } public AddressRecord Address { get; private set; } // Address sınıfını burada kullanıyoruz }
bashUser kubilay = new(Guid.NewGuid()); kubilay.Email = new(""); //Erişip değişiklik yapabiliyorum. Console.Write(kubilay.Name);
bashpublic sealed class User : Entity { public User(Guid CreatedTimeAssignedId) : base(CreatedTimeAssignedId) { } public Name Name { get; private set; } public Password Password { get; private set; } public Email Email { get; private set; } public AddressRecord Address { get; private set; } // Address sınıfını burada kullanıyoruz public void UpdateName(Name name) { Name = name; } }
bashUser kubilay = new(Guid.NewGuid()); kubilay.UpdateName(new("dddddddd")); //Doğrudan Erişip değişiklik yapamıyorum. Console.Write(kubilay.Name);
private set
olarak ayarlandı dışarıdan ulaşamıyoruz. User oluşurken nasıl Name,Password gibi değerleri assign edeceğiz?
Constructor ile tabiki.
bashpublic sealed class User : Entity { public User(Guid Id,Name name, Password password, Email email, AddressRecord address):base(Id) { Name = name; Password = password; Email = email; Address = address; } public Name Name { get; private set; } public Password Password { get; private set; } public Email Email { get; private set; } public AddressRecord Address { get; private set; } // Address sınıfını burada kullanıyoruz public void UpdateName(Name name) { Name = name; } }
bashUser kubilay = new(Guid.NewGuid(),new("Kubilay"),new("1233131"),new("kubilay@bozak.dev"),new("Türkiye","İstanbul","Test","Test Full Address","3124")); Console.Write(kubilay.Name); //Kubilay değerini döner kubilay.UpdateName(new("dddddddd")); //Doğrudan Erişip değişiklik yapamıyorum. Console.Write(kubilay.Name); //dddddddd değerini döner
bash//Id değerleri farklı User user1 = new(Guid.NewGuid(), new("Kubilay"),new("1233131"),new("kubilay@bozak.dev"),new("Türkiye","İstanbul","Test","Test Full Address","3124")); User user2 = new(Guid.NewGuid(), new("Kubilay"), new("22222222222"), new("kubilay2@bozak2.dev"), new("Türkiye2", "İstanbul", "Test", "Test Full Address", "3124")); //Id değerleri aynı User user3 = new(oneTimeCreatedGuid, new("Kubilays"), new("1233131"), new("kubilay@bozak.dev"), new("Türkiye", "İstanbul", "Test", "Test Full Address", "3124")); User user4 = new(oneTimeCreatedGuid, new("Kubilay"), new("22222222222"), new("kubilay2@bozak2.dev"), new("Türkiye", "İstanbul", "Test", "Test Full Address", "3124")); Console.WriteLine(user1.Name.Equals(user2.Name) +" Name değerleri user1 ve user 2"); //True döner Value Object'ler ve içerikleri aynı Console.WriteLine(user1.Address.Equals(user2.Address) +" Address değerleri user1 ve user 2"); //False döner içerikleri farklı Console.WriteLine(user1.Equals(user2) + " Userdeğerleri user1 ve user2"); //False döner çünkü User sınıfı Entity'dir ve Id'leri farklıdır. Console.WriteLine(user3.Name.Equals(user4.Name) + " Name değerleri user1 ve user 2"); //True döner Value Object'ler ve içerikleri aynı Console.WriteLine(user3.Address.Equals(user4.Address) + " Address değerleri user1 ve user 2"); //True döner içerikleri aynı Console.WriteLine(user3.Equals(user4) + " Userdeğerleri user1 ve user2"); //True döner çünkü User sınıfı Entity'dir ve Id'leri aynıdır.
Zorluk/Fark | Açıklama |
---|---|
👨💻 Kod biraz daha karmaşık görünür | Her alan için ayrı bir sınıf yazmak gerekir |
🧠 Öğrenme eğrisi | EF Core ve klasik yaklaşım alışkanlığı olanlar için alışmak zaman alır |
🧱 EF Core mapping ayarları gerekir | Value Object’leri map’lemek için Owned Entity kullanman gerekir (ama kolay bir ayardır) |
string
’di, domain kuralları yoktu.
İkinci yapıya geçince: