DDD-实体与估价对象



我读到了关于DDD的文章,我意识到有时一个实体可能是VO,或者VO可能是一个实体。你可以根据上下文知道哪一个更好。我检查了不同的例子。例如,购物车DDD示例。产品是一个聚合根,购物车是一个集合根,物品是购物车的一个实体,所以如果你想把产品添加到购物车中,你可以这样做:

$cart->addProduct(id $id, $name, $price)
class Cart
{
private items;
function addProduct(ProductId $id, ProductName $name, ProductPrice $price) {
this->items[] = new Item(
new ItemProductId($id->ToString()),
new ItemName($name->ToString()),
new ItemPrice($price->ToString()),
new ItemCartId(this->id->ToString())
);
}
}

我认为这是一个VO有两个原因:

  1. 您不能修改值的项(仅当产品价格已被修改存在将修改其价格的事件)
  2. 该项没有id,它具有的引用产品(ItemProductId)和购物车的引用(ItemCartId)

我读到了关于DDD的文章,我意识到有时一个实体可能是VO,或者VO可能是一个实体。你可以根据上下文知道哪一个更好。

通常它非常清楚什么是实体,什么是值对象。如果它包含在赋值时固定的数据,那么它就是一个值对象。例如订单聚合上的"订单地址"。下订单时,会设置地址。"地址"可能是用户聚合中的一个实体(即他的常用地址列表),但对于订单来说,它是一个值对象,因为当用户编辑或删除他的一个地址时,它不应该更改。

cart->addProduct(id $id, $name, $price)
class Cart
{
private items;
function addProduct(ProductId $id, ProductName $name, ProductPrice $price) {
this->items[] = new Item(
new ItemProductId($id->ToString()),
new ItemName($name->ToString()),
new ItemPrice($price->ToString()),
new ItemCartId(this->id->ToString())
);
}
}

这是一个非常糟糕的例子。为什么value对象应该是ItemPrice?这有什么特别的吗?为什么是字符串?价格通常只是一个数字值,但也涉及到一种货币,以字符串形式传递它有点比这更好。

最重要的是,它中有ItemCartIda) 将数据持久性知识泄漏到您的域中。事实上,它包含在this->items[]中,已经在实体(或聚合)和值对象之间建立了关系。ItemCartId在域中没有任何意义,除了关系数据库引擎(=持久性知识)需要它

我认为这是一个VO:有两个原因

您不能修改价值的项目(只有当产品的价格已被修改时,才会有事件修改其价格)。

你确定吗?为什么电子商务企业希望在卡上有价格?

价格仅供参考,在下订单之前可能会发生变化。与可用性相同。

很多用户把东西放进购物车,第二天再检查。在那个时候,价格可能会发生变化。

如果产品在放入购物车后的一段时间内价格上涨,任何公司都不会想以放入购物车时的价格出售产品。这将意味着经济损失。

购物车中的价格是信息性的,而不是强制性的。你需要知道公司的具体流程。

项目没有id,它有一个产品引用(ItemProductId)和一个购物车引用(ItemCartId)再一次为什么您认为ItemCartId属于Item对象?这泄露了持久性知识,因为它只对关系数据库系统很重要。

购物车中您真正需要的只是*产品或文章编号(不需要id,这通常是数据库知识)*数量

没有别的。如果您可能想在价格更改时更改用户,并显示旧价格和新价格,则也可以将价格(=货币值对象,而不是ItemPrice)作为与旧状态进行比较的值。

最后,也许也是最重要的一点:考虑一下购物车是否是一个集合(或者是否适合ddd)。

毕竟,大多数购物车只是一个价值袋,里面没有很多商业逻辑。真正的逻辑(检查真实价格、产品可用性、询问运输地点、计算税款和运输成本)发生在结账过程中,而不是在把东西放进购物车时。

例如,您可以查看容器上的eShops演示项目,该项目展示了一个基于微服务和ddd的示例购物服务。

一些微服务应用DDD(如订购微服务),而其他微服务则不应用(目录微服务或购物篮(cart)微服务)。

应用DDD并不意味着一切都需要使用DDD。如果它是简单的基于crud的服务,那么这些服务就不需要DDD。DDD在拥有复杂系统和复杂业务逻辑的情况下会增加价值。

目录两者都没有,它只是显示来自不同系统的数据(即ERP,另一方面可能使用DDD构建)。

我不明白你到底在问什么,但你提供的代码可以改进。

首先,我建议你读沃恩·弗农的红书https://www.amazon.co.uk/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577:您可以找到3章来描述如何定义实体、值对象和聚合,以及一些拇指规则。

其中一个建议是,保持聚合尽可能小,以提高性能,并使代码易于阅读和维护。想象一下,你有一个包含Post实体列表的博客聚合:如果你在一个聚合中管理所有这些实体,例如,当你想修改博客作者时,你被迫毫无理由地检索博客的所有帖子,这意味着你正在进行联接并降低应用程序的速度。聚合增长得越多,这些查询的连接速度就越慢。

因此,在购物车的情况下,我建议您在没有任何物品或产品的情况下构建购物车,相反,您可以将CartId添加到物品中。购物车不知道它包含哪些物品,但物品知道它们在哪个购物车中。

关于值对象:这是一个工具,允许您将一些验证和业务逻辑封装在一个由其状态而非id(类似于实体)表示的类中,因此在购物车的情况下,如果您在其中放入两瓶相同的水,您如何知道它们是不同的?你需要知道它们是不同的吗?如果它们在物理上(或逻辑上)不同,它们是不同的吗?如果它们的某些属性不同,它们又是不同的?

在我看来,在你的情况下,一个物品或产品是实体,因为它们没有测量任何东西,当你把一个物品放两次时,你实际上有两个不同的物品(你使用id来识别它们)。

这不是必要的,有时你可以使用值对象,有时也可以使用实体,这取决于你的上下文。一个很好的例子来理解差异在于金钱:

  1. 如果你想测量一个金额,例如10美元,可能一个价值对象会对你有用,因为你不在乎它是账单还是其他,你只想测量10美元;在这种情况下,如果你有10美元,那么如果你有一张或另一张钞票就不重要了,重要的是这是10美元,而不是5

  2. 如果出于任何原因(你需要为警方追踪资金),你需要识别不同的实物纸币,你应该使用一个实体,因为任何打印的纸币都有一个唯一的序列号,在这种情况下,10美元纸币实际上与另一张10美元纸币不同

希望这能帮助到你。

再见!

相关内容

  • 没有找到相关文章

最新更新