Can value objects exist without Entities?
Yes, they can since Value Objects should not refer to an Entity.
Value Object has no identity and two value objects are equal when their attributes are identical. Usually Value Objects are used to describe Money, Address, Weight types. But LoginAuth
can also be a Value Object with login
and password
attributes. It can also have and different other properties. Maybe expirationDate
, lastLoginDate
or something else...
Entity holds Value Objects usually. But even entity can't live without Aggregate Root if you want to persist it. The aggregate root should refer to the entity or to a list of entities.
Entities, Value Objects, Aggregates and Roots have their own specific role and usage in the Domain Model.
I have a value object LoginAuth which contains the User authentication
data for my secondary login system.
For every User it is optional to opt for the secondary login. So the
User entity does not hold the LoginAuth value object but instead, the
LoginAuth value object contains the User entity which it belongs to.
Domain model and database structure might be different.
- When you design Domain Model you try to represent relations that exist in the target domain.
- When you design database structure you think more about how data is stored, normalization and denormalization.
Domain entity User
can be designed the following way and SecondaryLogin
can be optional:
public class LoginAuth
{
public string Login { get; private set; }
public string Password { get; private set; }
public LoginAuth(string login, string password)
{
Login = login;
Password = password;
}
}
public class User
{
public LoginAuth PrimaryLogin { get; private set; }
public LoginAuth SecondaryLogin { get; private set; }
public User(string login, string password)
{
PrimaryLogin = new LoginAuth(login, password)
}
public User(
string login,
string password,
string secondaryLogin,
string secondaryPassword) : this(login, password)
{
SecondaryLogin = new LoginAuth(secondaryLogin, secondaryPassword);
}
}
If we start using ORM like Entity Framework or NHibernate they will generate the following table by default. They store Value Objects linked to an Entity together with it:
Users
=====================================================
Id (PK) | int | not null |
PrimaryLogin_Login | nvarchar(255) | null |
PrimaryLogin_Password | nvarchar(255) | null |
SecondaryLogin_Login | nvarchar(255) | null |
SecondaryLogin_Password | nvarchar(255) | null |
In one one of my projects I wanted to keep my database normalized and to store optional container for properties in separate table too.
Since my database is normalized, I store this value object in a
separate table where the user_id is the primary key (to ensure
uniqueness).
As you can see, my value object doesn't live inside an entity but
rather on it's own, but it does contain the entity it belongs to.
Because of NHibernate (Id column is required and tables are generated for entities only) it was needed to make it an Entity. But if no ORM is used and entities are loaded by yourself, you can store them as you wish.
ParentEntities
=====================================================
Id (PK) | int | not null |
Name | nvarchar(255) | not null |
OptionalEntities
=====================================================
Id (PK) | int | not null |
ParentEntityId (FK) | int | not null |
Login | nvarchar(255) | not null |
Password | nvarchar(255) | not null |
or it is also possible to make One-to-One relation that is not recommended and I'm not sure if it works for optional properties.
ParentEntities
=====================================================
Id (PK) | int | not null |
Name | nvarchar(255) | not null |
OptionalEntities
=====================================================
Id (PK) | int | not null |
Login | nvarchar(255) | not null |
Password | nvarchar(255) | not null |
In this case ParentEntities.Id
is equal to OptionalEntities.Id
when data is related to the same entity.
User
who has not opted for the secondary login will just havenull
in place of the property of thisLoginAuth
VO? And there is no other way to prevent entity havingnull
VO in it's valid state? – Forzando