diff --git a/docs/en/Domain-Driven-Design.md b/docs/en/Domain-Driven-Design.md index 9e0343d562..fe134a28e7 100644 --- a/docs/en/Domain-Driven-Design.md +++ b/docs/en/Domain-Driven-Design.md @@ -27,9 +27,9 @@ See the following documents to learn what ABP Framework provides to you to imple * **Domain Layer** * [Entities & Aggregate Roots](Entities.md) - * Value Objects * [Repositories](Repositories.md) * [Domain Services](Domain-Services.md) + * [Value Objects](Value-Objects.md) * Specifications * **Application Layer** * [Application Services](Application-Services.md) diff --git a/docs/en/Value-Objects.md b/docs/en/Value-Objects.md index 0c6c8d4424..f61e32245b 100644 --- a/docs/en/Value-Objects.md +++ b/docs/en/Value-Objects.md @@ -1,3 +1,75 @@ -## Value Objects +# Value Objects -TODO \ No newline at end of file +> An object that represents a descriptive aspect of the domain with no conceptual identity is called a VALUE OBJECT. +> +> (Eric Evans) + +Two [Entities](Entities.md) with the same properties but with different `Id`s are considered as different entities. However, Value Objects have no `Id`s and they are considered as equals if they have the same property values. + +## The ValueObject Class + +`ValueObject` is an abstract class that can be inherited to create a Value Object class. + +**Example: An Address class** + +````csharp +public class Address : ValueObject +{ + public Guid CityId { get; private set; } + + public string Street { get; private set; } + + public int Number { get; private set; } + + private Address() + { + + } + + public Address( + Guid cityId, + string street, + int number) + { + CityId = cityId; + Street = street; + Number = number; + } + + protected override IEnumerable GetAtomicValues() + { + yield return Street; + yield return CityId; + yield return Number; + } +} +```` + +* A Value Object class must implement the `GetAtomicValues()` method to return the primitive values. + +### ValueEquals + +`ValueObject.ValueEquals(...)` method is used to check if two Value Objects are equals. + +**Example: Check if two addresses are equals** + +````csharp +Address address1 = ... +Address address2 = ... + +if (address1.ValueEquals(address2)) //Check equality +{ + ... +} +```` + +## Best Practices + +Here are some best practices when using Value Objects: + +- Design a value object as **immutable** (like the Address above) if there is not a good reason for designing it as mutable. +- The properties that make up a Value Object should form a conceptual whole. For example, CityId, Street and Number shouldn't be separate properties of a Person entity. This also makes the Person entity simpler. + +## See Also + +* [Entities](Entities.md) \ No newline at end of file diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 29d658aee4..a3dcc17dcf 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -340,7 +340,8 @@ "path": "Entities.md" }, { - "text": "Value Objects" + "text": "Value Objects", + "path": "Value-Objects.md" }, { "text": "Repositories", diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/Address.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/Address.cs new file mode 100644 index 0000000000..0fdc29280f --- /dev/null +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/Address.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Domain.Values +{ + public class Address : ValueObject + { + public Guid CityId { get; } + + public string Street { get; } + + public int Number { get; } + + private Address() + { + } + + public Address( + Guid cityId, + string street, + int number) + { + CityId = cityId; + Street = street; + Number = number; + } + + //Requires to implement this method to return properties. + protected override IEnumerable GetAtomicValues() + { + yield return Street; + yield return CityId; + yield return Number; + } + } +} diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/ValueObject_Tests.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/ValueObject_Tests.cs new file mode 100644 index 0000000000..57cc2a6c60 --- /dev/null +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/ValueObject_Tests.cs @@ -0,0 +1,30 @@ +using System; +using Shouldly; +using Xunit; + +namespace Volo.Abp.Domain.Values +{ + public class ValueObject_Tests + { + [Fact] + public void ValueObjects_With_Same_Properties_Should_Be_Equals() + { + var cityId = Guid.NewGuid(); + var address1 = new Address(cityId, "Baris Manco", 42); + var address2 = new Address(cityId, "Baris Manco", 42); + + address1.ValueEquals(address2).ShouldBeTrue(); + } + + [Fact] + public void ValueObjects_With_Different_Properties_Should_Not_Be_Equals() + { + var cityId = Guid.NewGuid(); + + var address1 = new Address(cityId, "Baris Manco", 42); + var address2 = new Address(cityId, "Baris Manco", 42); + + address1.ValueEquals(address2).ShouldBeFalse(); + } + } +}