【问题标题】:ddd - How to properly identify Value Objects?ddd - 如何正确识别值对象?
【发布时间】:2016-07-20 12:16:33
【问题描述】:

我正在为我的实体建模,并且为此苦苦挣扎了很长时间。这是我的Person 实体:

Person
    ID
    Name
    Email
    Password
    City
    Phone
    Biography
    Rating
    Description

我曾尝试将这些属性划分为值对象,但到目前为止我只能将它们转换为 VO(例如,City 是由城市名称和国家名称组成的 VO)。

我是否应该尝试创建更大的 VO,例如将 EmailPassword 组合成一个 Credentials VO?我是否在分离到 VO 方面走得太深了?

非常感谢任何帮助

[编辑]

经过一些讨论,似乎最好的解决方案是将每个属性都保留在自己的 VO 中,除了应分组在“凭据”VO 中的电子邮件和密码。

【问题讨论】:

    标签: domain-driven-design value-objects


    【解决方案1】:

    值对象是值唯一标识它们的事物,即相等是由值完成的,而不是通过显式属性(如 ID)来提供唯一性。如果所有字段都相等,则两个值对象相等

    在尝试识别它们时,请遵循领域专家使用的语言。他们在讨论人物时使用什么词?它们是指凭据,还是电子邮件和密码?

    还要找出总是一起使用的属性组。例如,如果 Password 总是与 Email 一起使用,那么将它们分组到 Credentials 对象中是有意义的,因为您可以在那里推送行为(即 Credentials.Validate())

    [更新]

    以下所有属性都是值对象的候选对象取决于您需要强制执行的不变量

    姓名

    • 名称是否有最小值/最大值?
    • 是否有不允许使用的字符?

    电子邮件

    • 这是一个有效的电子邮件地址
    • 两个人可以拥有相同的电子邮件地址吗?

    密码

    • 最小/最大长度?
    • 必填字符?
    • 无效字符?

    电话

    • 这是一个有效的电话号码吗?
    • 你们如何处理国际区号?

    评分

    • 评分是否有最小值和最大值?
    • 如何计算? (计算出来了吗?)

    说明

    传记

    城市

    等等……

    如果您为上述概念创建值对象而不是使用原始值(如 int 或字符串),则可以将业务规则封装在值对象中。

    如果您同时使用这两个东西,您可能希望将电子邮件和密码组合成一个凭据值对象。即使用凭据等登录...如果您需要在 Credentials 对象之外使用电子邮件,您仍然可以访问 Credentials.Email。

    【讨论】:

    • 问题是,我有点想通过这个项目开始使用 DDD,而且我是一个人工作,所以我既是领域专家又是开发人员,这很友好难以使用无处不在的语言
    • 然后尝试第二种方法 - 经常一起使用的东西
    • 问题是某些字段可以在没有其他字段的情况下使用。以您为例,电子邮件可以在没有密码的情况下使用,例如显示在个人资料页面上,这就是我为此苦苦挣扎的原因。
    • 我刚刚看到您更新的答案。问题是我想强制执行尽可能多的不变量。例如:名称必须是à VO,因为它包含名字、中间名和姓氏。城市,因为它也包含国家代码等。我想我仍然无法在 VO 中分离属性,即使使用我的特定用例
    • 我不明白 - 为什么你不能按照你的描述做?
    【解决方案2】:

    你所拥有的东西看起来像是一种数据结构 (CRUD)。在适当的 DDD 中,您从“创建人员”之类的业务案例开始,然后找出代表人员概念模型。

    模型由一组组件和业务规则定义。您的组件通常是 VO,因为它们是代表次要概念的模型,可以表示为没有明确标识的简单或复合值,并且它们封装了特定的业务约束。例如,Email VO 确保您拥有有效的电子邮件值。

    您应该让域告诉您创建更大的 VO 是否有意义;通常你从一个 VO 开始,然后发现它是由其他 VO 组成的。一般来说,我们从上到下。你可以see here一个建模例子。

    如果您没有领域专家,这很困难,但您仍然可以尝试在业务案例中进行思考,并为每个案例确定任何特定模型。如果您最终得到主要是简单的结构和一些数据验证规则,那么您可能有一个足够简单的域来使用 CRUD 方法。

    【讨论】:

    • 在关联账户建模示例中,如何实现借方不得超过账户余额的不变量?你会为此依赖读取模型,从而使其最终保持一致吗?我想必须对金额进行某种限制,这样我们就不必补偿高得离谱的钱。
    • @plalx 您所说的是域服务的一部分,而不是传输聚合。 Transfer 不知道 AccountBalance。业务规则可以允许更高的金额 => 你会得到一笔不错的透支费或阻止它。但我的示例只关注聚合,而不是整个业务案例。
    • 我的意思是,没有办法防止透支交易,因为转移是唯一起作用的聚合体,没有余额的概念。想象一下,为了避免极高的透支,您每天将账户转账金额最大化为 1000 美元。您将如何处理有人通过执行多个并发传输请求来利用竞争条件并因此累积超过每天 1000 美元的情况?我认为这是银行必须遵循的模型,因为它们具有分布式特性,但是暴露不变量比防止透支要复杂得多。
    • 你可以删除CQRS,你仍然有同样的问题。而您所说的基本上是脚本攻击(您不能手动执行该操作),这是一个不同的问题。唯一的解决方案是同步执行所有事务,这意味着性能非常低。但是银行不会有几个同时交易的问题。如果需要,它可以在以后发布补偿措施。
    【解决方案3】:

    不要试图强加特定的领域模型结构,您需要从用例的角度来处理领域建模。

    我是否应该尝试创建更大的 VO,例如将电子邮件和密码放在一个凭据 VO 中?

    只有当两者倾向于一起使用时,您才应该这样做。如果他们不这样做,留下他们就好了。

    请注意,有时如果需要支持的不变量数量足以证明引入新概念的合理性,则将单个属性提取到其自己的值对象中是有意义的。更多详情请查看this article

    【讨论】:

      【解决方案4】:

      在你的领域中的概念并不总是很清楚是一个价值对象或实体,不幸的是没有客观的属性可以用来了解它是否一个概念是一个价值对象完全取决于问题领域,一个概念可以是一个领域模型中的实体或另一个领域模型中的值对象。大多数时候,货币类是价值对象,但并非所有货币对象都是价值对象。你怎么能找到一个值对象?首先,您应该检查身份的概念是否在结构上相等(这意味着如果对象值相同,而不是对象的 ID,则这意味着两个对象相同),您可以安全地替换具有相同属性集的类的实例。这是一个好兆头,这个概念是一个值对象。

      考虑值对象的一个​​好方法是考虑它是否是一个整数,你真的关心整数 5 是否与你在其他函数中使用的 5 相同吗?绝对不是您的应用程序中的所有 5 个都是相同的,无论它们是如何实例化的。这使得整数本质上是一个值对象,现在问你自己域中的对象就像一个整数?如果答案是肯定的,那么它是一个值对象。此外,值对象是不可变的,并且作为实体更轻量级。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-15
        相关资源
        最近更新 更多