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



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

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

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

如您所见,我的价值对象不存在于实体内,而是独立存在,但它确实包含它所属的实体。我的问题是:

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

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

    每个LoginAuth都应该是唯一的(每个LoginAuth只允许一个唯一的User(,所以这个 VO 不会有任何相等的。

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

价值对象可以在不存在于实体内部的情况下存在吗?

是的,他们可以。某些临时值对象只能在计算中使用,而永远不会持久化或与实体相关。

但是,这不是我们在这里谈论的对象类型。 LoginAuth显然与User有关系,并随之而来。

要确定这种关系的方向(你所说的"住在里面与住在外面"(,你必须考虑获得参考。在 DDD 中,只能从存储中解除冻结聚合根(通过存储库(。获得对根的引用后,可以导航到其聚合中的实体和值对象。

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

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

如果您采用 CQRS 方法,并且"登录系统"仅意味着读取LoginAuth,则可以绕过存储库并使用读取模型直接获取所需的数据。

但是,这仅涵盖阅读方面。我想您仍然希望能够在某个时候更改用户的登录身份验证,因此仍然需要为其进行写入端聚合突变。

首先要记住的是,当内部数据相等(而不是引用(时,Value Object 必须相等。

问题 1:如果您有两个LoginAuth引用,它们保存不同的User对象(具有相同的数据(,这将使两个LoginAuth不相等。

问题

2:如果有人改变了第一个用户引用的状态?但第二个用户引用仍然是一样的,就会发生大问题。你明白吗?

因为User是一个必须具有id的实体,所以LoginAuth只能在里面保存id值,而不是整个User对象,然后你可以LoginAuth放入你的数据库中或序列化,通过你的网络发送,任何你想要的。

值对象可以在不存在于实体内部的情况下存在吗?这样做是可以的,但你还没有尝试打破值对象的概念。

-------更新--------

也许这需要是一个实体? 没必要。您说您规范化了数据库并将LoginAuth存储在单独的表中,假设login_auth表,该表将Userid存储在第 user_id 列中,以确保单个User只有一个LoginAuthuser_id作为主键(或唯一键(并在用于将LoginAuth放入数据库的类中进行一些双重检查

没有实体,值对象可以存在吗?

是的,它们可以,因为值对象不应引用实体。

对象没有标识,当两个值对象的属性相同时,它们相等。通常值对象用于描述货币,地址,权重类型。但LoginAuth也可以是具有loginpassword属性的值对象。它还可以具有不同的其他属性。也许expirationDatelastLoginDate或其他什么...

实体通常保存值对象。但是,如果要持久化聚合根,即使实体也无法生存。聚合根应引用实体或实体列表。

实体、值对象、聚合和根在域模型中具有自己的特定角色和用法。

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

对于每个用户,选择辅助登录是可选的。所以 用户实体不保存登录身份验证值对象,而是保存 登录身份验证值对象包含它所属的用户实体。

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

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

域实体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);
    }
}

如果我们开始使用像实体框架或 NHibernate 这样的 ORM,默认情况下它们将生成下表。它们存储链接到实体的值对象以及实体:

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     |

在我的一个项目中,我想保持数据库规范化,并将属性的可选容器存储在单独的表中。

由于我的数据库已规范化,因此我将此值对象存储在 以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 |

或者也可以建立不推荐的一对一关系,我不确定它是否适用于可选属性。

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

相关内容

最新更新