可以在没有实体的情况下存在值对象

时间:2014-08-22 15:16:43

标签: entity domain-driven-design domain-model value-objects

我有一个值对象LoginAuth,其中包含我的辅助登录系统的User身份验证数据。

对于每个User可选选择辅助登录。因此User实体不包含LoginAuth值对象,而是LoginAuth值对象包含它所属的User实体。

由于我的数据库已规范化,因此我将此值对象存储在一个单独的表中,其中user_id是主键(以确保唯一性)。

正如您所看到的,我的值对象并不存在于实体内部,而是存在于实体内部,但 包含它所属的实体。我的问题是:

  • 可以在不居住在实体内的情况下存在价值对象吗?

  • 也许这需要是一个实体?

    每个LoginAuth都应该是唯一的(每User只允许一个唯一的LoginAuth),因此这个VO不会有任何等价。

    < / LI>

注意:我的域包含此登录系统的应用程序逻辑。只是它应该处理的数据。它的应用程序逻辑位于我的模型层的应用程序层中。

3 个答案:

答案 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有关系,并且与之保持一致。

要确定这种关系的方向(你所指的是&#34;生活在室内和生活在外面&#34;),你必须考虑获得参考。在DDD中,只有聚合根可以从存储中重新水合(通过存储库)。一旦有了对Root的引用,就可以导航到Aggregate中的Entities和Value Objects。

如果您在该点上遵循DDD,则不允许直接从数据库获取值对象。您必须加载整个聚合,然后通过实体获取对VO的引用。因此,您需要在实体中存储对值对象的引用,或者至少在实体中内置可以生成VO的内容。在您的示例中,这会导致User持有LoginAuth。反过来也不会起作用。

  

注意:我的域名不包含此登录的应用程序逻辑   系统。只是它应该处理的数据。应用程序逻辑   它位于我的模型层的应用层中。

如果您采用CQRS方法并且如果&#34;登录系统&#34;只是意味着阅读LoginAuth,您可以绕过存储库并使用读取模型直接获取您想要的数据。

但是,这只涵盖了事物的阅读方面。我想您仍然希望能够在某个时刻更改用户的LoginAuth,因此仍然需要一个写入端聚合突变。

答案 2 :(得分:1)

  

可以在没有实体的情况下存在值对象吗?

是的,他们可以因为Value Objects不应该引用实体。

Value Object没有标识,并且their attributes are identical 时两个值对象相等。通常,值对象用于描述货币,地址,权重类型。但LoginAuth也可以是具有loginpassword属性的值对象。它还可以具有不同的其他属性。可能是expirationDatelastLoginDate或其他...

实体通常持有值对象。但是,如果你想坚持下去,那么偶数实体就不能没有Aggregate Root aggregate root应该引用实体或实体列表。

Entities, Value Objects, Aggregates and Roots在域模型中有自己的特定角色和用法。

  

我有一个包含用户身份验证的值对象LoginAuth   我的辅助登录系统的数据。

     

对于每个用户,可选择选择辅助登录。所以   用户实体不包含LoginAuth值对象,而是包含   LoginAuth值对象包含它所属的User实体。

域模型和数据库结构可能不同。

  • 设计域模型时,您尝试表示目标域中存在的关系。
  • 在设计数据库结构时,您会更多地考虑数据的存储方式,规范化和非规范化。

可以按以下方式设计域实体UserSecondaryLogin可以是可选的:

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