我有一个值对象LoginAuth
,其中包含我的辅助登录系统的User
身份验证数据。
对于每个User
,可选选择辅助登录。因此User
实体不包含LoginAuth
值对象,而是LoginAuth
值对象包含它所属的User
实体。
由于我的数据库已规范化,因此我将此值对象存储在一个单独的表中,其中user_id
是主键(以确保唯一性)。
正如您所看到的,我的值对象并不存在于实体内部,而是存在于实体内部,但 包含它所属的实体。我的问题是:
可以在不居住在实体内的情况下存在价值对象吗?
也许这需要是一个实体?
每个LoginAuth
都应该是唯一的(每User
只允许一个唯一的LoginAuth
),因此这个VO不会有任何等价。
注意:我的域不包含此登录系统的应用程序逻辑。只是它应该处理的数据。它的应用程序逻辑位于我的模型层的应用程序层中。
答案 0 :(得分:1)
首先要记住的是,当内部数据相等(不是引用)时,Value Object必须相等。
问题1:如果你有两个LoginAuth
引用,它包含User
的不同对象(具有相同的数据),会使两个LoginAuth
不相等。
问题2:如果有人改变了第一个用户引用的状态?但第二个用户参考仍然是相同的,大问题将会发生。你明白了吗?
由于User
是必须包含id
的实体,因此LoginAuth
只能包含id
值,而不是整个User
对象,那么您能够将LoginAuth
放入您的数据库或序列化,通过您的网络发送,无论您想要什么。
可以在没有居住在实体内部的情况下对值对象进行存在吗?可以这样做,但您还没有尝试打破价值对象的概念。
-------更新--------
也许这需要成为一个实体?这不是必需的。您说您将数据库标准化并将LoginAuth
存储在单独的表中,让我们说login_auth
表,其中id
User
存储在user_id
列中,要确保单个User
只有一个LoginAuth
,请将user_id
作为主键(或唯一)并在课堂上进行一些双重检查,用于放置LoginAuth
进入数据库
答案 1 :(得分:1)
可以在没有居住在实体内的情况下存在价值对象吗?
是的,他们可以。一些短暂的值对象只能用于计算,并且永远不会持久存在或与实体相关。
但是,这不是我们在这里讨论的对象类型。 LoginAuth
显然与User
有关系,并且与之保持一致。
要确定这种关系的方向(你所指的是"生活在室内和生活在外面"),你必须考虑获得参考。在DDD中,只有聚合根可以从存储中重新水合(通过存储库)。一旦有了对Root的引用,就可以导航到Aggregate中的Entities和Value Objects。
如果您在该点上遵循DDD,则不允许直接从数据库获取值对象。您必须加载整个聚合,然后通过实体获取对VO的引用。因此,您需要在实体中存储对值对象的引用,或者至少在实体中内置可以生成VO的内容。在您的示例中,这会导致User
持有LoginAuth
。反过来也不会起作用。
注意:我的域名不包含此登录的应用程序逻辑 系统。只是它应该处理的数据。应用程序逻辑 它位于我的模型层的应用层中。
如果您采用CQRS方法并且如果"登录系统"只是意味着阅读LoginAuth
,您可以绕过存储库并使用读取模型直接获取您想要的数据。
但是,这只涵盖了事物的阅读方面。我想您仍然希望能够在某个时刻更改用户的LoginAuth,因此仍然需要一个写入端聚合突变。
答案 2 :(得分:1)
可以在没有实体的情况下存在值对象吗?
是的,他们可以因为Value Objects不应该引用实体。
Value Object没有标识,并且their attributes are identical 时两个值对象相等。通常,值对象用于描述货币,地址,权重类型。但LoginAuth
也可以是具有login
和password
属性的值对象。它还可以具有不同的其他属性。可能是expirationDate
,lastLoginDate
或其他...
实体通常持有值对象。但是,如果你想坚持下去,那么偶数实体就不能没有Aggregate Root。 aggregate root应该引用实体或实体列表。
Entities, Value Objects, Aggregates and Roots在域模型中有自己的特定角色和用法。
我有一个包含用户身份验证的值对象LoginAuth 我的辅助登录系统的数据。
对于每个用户,可选择选择辅助登录。所以 用户实体不包含LoginAuth值对象,而是包含 LoginAuth值对象包含它所属的User实体。
域模型和数据库结构可能不同。
可以按以下方式设计域实体User
,SecondaryLogin
可以是可选的:
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);
}
}
如果我们开始使用ORM,如Entity Framework或NHibernate,它们将默认生成下表。它们存储与实体链接的值对象:
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 |
在我的一个项目中,我希望保持数据库规范化,并在单独的表中存储属性的可选容器。
由于我的数据库已规范化,我将此值对象存储在a中 user_id是主键的单独表(以确保 独特性)。
正如您所看到的,我的值对象并不存在于实体内部 而是它本身,但它确实包含它所属的实体。
由于NHibernate(需要Id列并且仅为实体生成表),因此需要将其设为实体。但如果没有使用ORM并且您自己加载了实体,则可以按照自己的意愿存储它们。
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 |
或者也可以制作不推荐的One-to-One relation,我不确定它是否适用于可选属性。
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 |
在这种情况下,当数据与同一实体相关时,ParentEntities.Id
等于OptionalEntities.Id
。