Reputation: 2301
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.
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. My question is:
Can value objects exist without living inside entities?
Perhaps this would need to be an entity?
Each LoginAuth
is supposed to be unique (only a unique User
is allowed per LoginAuth
), so there won't be any equals of this VO.
Note: My domain does not contain the application logic of this login system. Just the data it's supposed to handle. The application logic of it resides in the application layer of my model layer.
Upvotes: 5
Views: 1991
Reputation: 15737
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.
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.
Upvotes: 1
Reputation: 14064
Can value objects exist without living inside entities?
Yes, they can. Some ephemeral Value Objects can be used in computations only and never persisted or related to an Entity.
However, this is not the type of object we're talking about here. LoginAuth
obviously has a relationship to User
and is persisted along with it.
To determine the direction of that relationship (what you're referring to as "living inside vs living outside"), you have to think in terms of obtaining references. In DDD, only Aggregate Roots can be rehydrated from storage (via Repositories). Once you have a reference to a Root, you can navigate to the Entities and Value Objects in its Aggregate.
If you follow DDD on that point, you aren't allowed to fetch a Value Object directly from the database. You have to load a whole aggregate, then obtain a reference to the VO through an Entity. Thus, you need to store a reference to the Value Object in an Entity, or at least have something built into the Entity that can produce the VO. In your example, this results in User
holding the LoginAuth
. The reverse alone wouldn't work.
Note: My domain does not contain the application logic of this login system. Just the data it's supposed to handle. The application logic of it resides in the application layer of my model layer.
If you take a CQRS approach to this and if the "login system" only implies reading the LoginAuth
, you could bypass Repositories and use a Read Model to get the data you want directly.
However, this only covers the read side of things. I guess you would still want to be able to change a User's LoginAuth at some point and therefore still need a write-side Aggregate mutation for it.
Upvotes: 5
Reputation: 529
The first things you have to remember is that Value Object must be equal when data inside is equal (not the reference).
Problem 1: If you have two LoginAuth
reference which hold different object of User
(which has the same data) that would make two LoginAuth
unequal.
Problem 2: if someone alter state of first user reference? but second user reference still be the same, big problem gonna be happen. Do you understand?
Because User
is an entity that has to have id
, so LoginAuth
can only hold id
value inside, not whole User
object, then you able to put LoginAuth
into your database or serialize, send across your network, whatever you want.
Can value objects exist without living inside Entities? It's ok to do that, but you haven't try to break the concept of value object.
------- UPDATE --------
Perhaps this would need to be an entity? It's not necessary. You said that you normalized database and store LoginAuth
in separate table, let's say login_auth
table, which store id
of User
in column user_id
, to make sure a single User
have only one LoginAuth
, make user_id
as primary key (or unique) and make some double check in class which you use to put LoginAuth
into database
Upvotes: 1