首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >DDD -价值对象的生命周期:验证和持久性

DDD -价值对象的生命周期:验证和持久性
EN

Stack Overflow用户
提问于 2021-10-14 08:34:52
回答 2查看 522关注 0票数 0

我明白什么是VO (不变的,没有身份的,.)。但我有几个问题来自与我的同事的讨论。

A)验证--它应该有多精确?

我应该对VO进行哪些类型的验证?只有最基本的还是全部?VO的一个很好的例子是带有regexp验证的Email。我看过很多次了。我已经开发了几个大中型应用程序,而regexp还不够好,因为:

exist

  • system-B:
  • 系统-A:电子邮件的域名被验证了,例如在无效电子邮件中的test@gmali.com,因为域gmali.com
  • ,我们有被禁止的“临时电子邮件服务”域的列表(服务),因为我们希望避免“伪造帐户”

我无法想象将这种验证应用到VO中,因为它需要网络通信,而VO会很复杂(而且速度慢)。

B)验证:名称,标题,字符串.长度是VO?的一部分吗?

程序员可以使用老好的string数据类型.我可以将VO想象为NotEmptyString,但创建价值对象的好方法是:

  • FirstName (长度为limitation)
  • Surname的非空字符串(具有长度限制的非空字符串)

FirstNameSurname之间没有区别,因为在应用程序中,我们无法确定某个人是否在形式上交换名字和姓氏。Robert可以是名字,也可以是姓.

代码语言:javascript
运行
复制
class Person 
{
  private string $firstName; // no VO for firstName
  // or 
  private FirstName $firstName;  // VO just for firstName & length validation
  // or
  private NotEmptyString $firstName; // VO - no max length validation
  // or
  private StringLength50 $firstName; // same as FirstName, different name for sharing 
}

哪种方法是最好的,为什么?

C) VO的副本:为实体参数提供“类型安全”?

这一点与以前的观点相似。

创建这样的类是很好的实践吗:

代码语言:javascript
运行
复制
class Surname extends Name
{

}

class FirstName extends Name
{

}

只是“化名”VO?

D)持久性:读取存储的VO

这一点与第一个问题密切相关:A)验证--它应该有多精确?。我坚信存储在我的“存储引擎”(DB)中的内容是有效的--没有问题。当在“持久性步骤”期间验证所有内容时,我不认为有什么理由再次验证VO。即使是复杂的/写得不好的regexp也可能是性能杀手--在用户电子邮件中列出N-数百个。我在这里迷路了。我应该只验证基本内容并在持久化和阅读期间使用相同的VO,还是应该为这些情况单独使用两个VO?

E)持久性/管理:系统中类似于“上帝”的东西。

根据我的经验:在实单词系统中,具有更高权限的用户有时可以通过验证规则,这同样与A点)示例有关:

  • you (系统的常规用户)可以从今天起在线预订最多30天,
  • 管理用户可以在线预订到任何日期

我应该只使用Date / FutureDate VO还是什么?

F)持久化:映射到DB数据类型

紧密绑定VO和DB (存储引擎)数据类型是一种良好的实践吗?

如果FirstName只能有50个字符,那么应该定义/映射到VAR_CHAR(50)

谢谢。

EN

回答 2

Stack Overflow用户

发布于 2021-10-15 21:32:39

A)验证--它应该有多精确?

这不是精确的问题,而是不变量和责任。值对象(VO)不可能对电子邮件地址是否存在具有权威。这是一个变化的事实,不能被VO控制。即使您有如下代码:

var emailAddress = EmailAddress.of('some@email.com', emailValidityChecker);

地址可能几分钟后就不存在了,用户可能永远丢失了他的帐户密码,等等。

那么EmailAddress应该代表什么呢?它应该确保地址的“格式”使其成为域中可用的、有用的地址

例如,在一个负责交税提醒的系统中,我必须使用Exchange,而且它不能支持某些电子邮件格式,比如“本地部分中带有”前导点、尾点或连续点“的电子邮件格式(接受了我所做的确切评论)。

尽管这在理论上是一个技术问题,但这意味着我们的系统不能摄入这样的电子邮件地址,而且它们对我们完全没用,所以ValidEmailAddress VO不接受那些早期失效的邮件地址(否则它会在链上产生假阳性)。

B)验证:名称,标题,字符串.长度是VO的一部分吗?

我会的,即使这样的长度有时会觉得有些武断或基础设施驱动。然而,我认为可以肯定地说,一个500个字符的名字肯定是个错误。此外,使用合理范围进行验证可以防止攻击(例如,1GB的名称??)。有些人可能会认为这纯粹是一个基础设施问题,并且会将验证放在另一层,但我不同意,我认为这种区分没有帮助。

长度规则并不总是任意的,例如不能超过280个字符的TweetMessage,这是一个域规则。

这是否意味着系统中每一个可能的字符串都必须有一个VO?老实说,我强迫后背害怕过度使用VOs和边缘,转而痴迷于VO,而不是原始的痴迷,但在几乎所有的场景中,我都希望我能花些时间把那该死的字符串打包起来。

务实点,但我认为不充分使用VOs比过度使用VOs更有害。

C) VO的副本:为实体参数提供“类型-安全”?

我很可能不会为了重用而扩展Name。很可能没有地方需要将SurenameFirstName交换,所以多态性也是非常无用的。但是,显式类型可能有助于将"surename“替换为”name“,反之亦然。

不管显式类型是否有用,这里更有用的方法可能是在FullName VO下聚合这两种类型,从而增加凝聚力。

请注意,对许多国际制度来说,过于严格的名称政策是一个巨大的痛点。

D)持久性:读取存储的VO

持久化数据处于“安全”端,当加载到内存中时,不应该再次验证。在补充我们的VOs时,您应该能够绕过验证路径。

E)持久性/管理:系统中类似于“上帝”的东西。

VOs很好地执行了它们的“不变量”。根据定义,不变量在给定的上下文中不会发生变化。事实上,当说“始终有效”的方法不起作用时,许多人误解了这一点。

也就是说,即使是系统管理员也很可能无法在past中做出新的保留,所以这可能是ReservationDate的一个不变变量。最终,您很可能在它们所属的上下文中提取其他规则。

F)持久化:映射到DB数据类型

我认为在领域中反映DB限制比在DB中反映域限制更重要。如果您的DB只接受50字符,并且超过了这个值,那么有些系统只会出现非常神秘的错误消息,甚至不会告诉您哪一列溢出。在域中进行验证将有助于更快地进行调试。但是,您不必一定要匹配DB中的域规则。

票数 1
EN

Stack Overflow用户

发布于 2021-10-16 10:26:11

DDD和任何其他设计一样,是一个绘制线条和制定抽象规则的问题。有些规则可能非常严格,而另一些规则可能在某种程度上是流利的。重要的是尽可能保持一致性,而不是努力建立最终的、不可战胜的领域。

验证--它应该有多精确?

“重”验证不应发生在VO内部。VO的性质与其封装的原语并没有很大的不同,因此验证应该独立于外部因素。请记住,即使是原语(如byte )也可能在内部进行验证:当指定值大于255的字节变量时,就会出现异常(有时甚至是编译错误)。

另一方面,高级验证属于流部分(用例/交互/命令处理程序),因为它们涉及到VO原语范围之外的操作,例如查询数据库或调用API。例如,您可以从数据库中查询有效的电子邮件提供程序列表,检查列表中是否包含VO的提供程序,如果不包含,则抛出异常。这是简单的流动。

当然,您可能会决定有一个内存中静态的电子邮件提供者列表,在这种情况下,将其保存在VO中并根据该列表检查其原语将是完全有效的。不需要与外界交流,一切都是“本土化”。它是可伸缩的吗?可能不是。但是它遵循一条DDD规则,即VO不应该与外部资源“说话”。

验证:名称,标题,字符串.长度是VO的一部分吗?

与其他DDD概念一样,VOs应该“大声说出”您的业务域,这意味着它们应该表示业务语义。这就是为什么FirstNameSurnameStreetName是好名字的原因,而NotEmptyString则不那么可取,因为它传递的是技术而不是业务细节。

例如,如果您的业务规定,具有超过50个字符长度的名称的客户的地址将不同于使用小于50个字符的名称的客户,那么您可能应该有两个VO,例如LongFirstNameShortFirstName

的确,几个VO可能需要完全相同的验证,例如StreetNameCityName都必须以大写开头,长度不能超过100。这是否意味着我们必须努力避免以“可重用性”的名义进行重复?我会说不,特别是如果避免重复意味着拥有一个名为CapitalHeadStringUpTo100Characters的VO。这样的名字没有商业目的。此外,如果CityName突然需要额外的验证,那么将CapitalHeadStringUpTo100Characters分解为两个VO可能需要做很多工作。

VO的

副本:为实体参数提供“类型-安全”?

继承是由开发平台提供的一种工具,使用它是完全可以的,但只会使事情变得混乱或过于抽象。记住,VO只表达了一个特定的领域-接近原则。另一方面,多态OOP原则(当然可以应用于DDD应用程序)与抽象概念(即基类)紧密结合,我认为它应该更适合实体模型部分。

顺便说一句,您可以在web上找到一个基本VO类的几个实现。

持久化:读取存储的VO

如果同一验证反复发生在单个用例的不同点上,系统设计就不那么重要了。除非您有理由相信您的数据库可以被外部组件更改,否则在不进行重新验证的情况下从数据库中重构实体就足够了。还请记住,一个典型的实体可以嵌入至少一个VO,这是在“持久化步骤”(在构造实体时)和在“读取步骤”中使用的相同VO (在重构时)。

持久性/管理:系统中类似于“上帝”的东西。

多租户应用程序可以了解多种类型的用户。软件不关心一个用户是否比另一个用户更强大,它只受规则的约束。您是否选择使用VO FutureDate设置单个通用实体FutureDate,还是选择两个实体UserAdmin和VOs FutureDate ( null)、FutureDate (可空),这一点我们不太感兴趣。重要的是,可以通过智能地使用依赖项注入来实现多租户:系统标识用户权限,并推断要注入哪些工厂、服务或验证。

持久化:映射到DB数据类型

这实际上取决于DDD字段中的成熟度级别。应用程序将始终存在bug,如果您选择设计宽松的数据库,您应该对您的业务的容错级别有一些线索。

除此之外,请记住,无论您在其中投入了多少精力,您的数据库可能永远无法反映完整的业务不变量:将单个VO限制在某个长度是容易的,但是设置涉及多个VOs的规则(即一个VO的有效性依赖于另一个VO)就不那么方便了。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69567550

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档