Model DDD-作为ID值对象的非规范化实体?

Model DDD-作为ID值对象的非规范化实体?,model,domain-driven-design,denormalization,value-objects,Model,Domain Driven Design,Denormalization,Value Objects,假设我们在仓库上下文中有一个订单。我们有一个客户正在接受订单 虽然在DB中,客户机有一个ID和一组参数,但我在订单中将其建模为一个值对象: class Order extends AggregateRoot { private client: Client; private shipping: Shipping; private items: Array<OrderItem>; } class Client extends ValueObject {

假设我们在仓库上下文中有一个订单。我们有一个客户正在接受订单

虽然在DB
中,客户机
有一个ID和一组参数,但我在订单中将其建模为一个值对象:

class Order extends AggregateRoot {
    private client: Client;
    private shipping: Shipping;
    private items: Array<OrderItem>;
}

class Client extends ValueObject {
    public readonly name: string;
    public readonly contactPhone: string;
    public readonly contactEmail: string;
}
然而,这里有一个令人困惑的地方:现在它看起来像一个实体(毕竟,我本质上是将一个实体从不同的上下文中反规范化为一个值对象)。“身份”在这里没有真正的意义,因为我在检查
客户端
是否平等时,不会关心
客户端ID

换句话说:就所有实际目的而言,仓库并不关心客户身份。此外,仓库不能更改任何客户详细信息。但是,存在着一种隐含的理解,即我们向同一个客户发货,即具有相同身份的客户

作为一个价值对象,我是否能够很好地建模
客户机
?这是一个常见的陷阱吗?我只是将“实体上的值对象”规则提升到一个绝对级别?

通常的答案是,您通过id引用客户端,而不是缓存其属性

class Order extends AggregateRoot {
    private clientId: DomainID; // or ClientID, not essential
    private shipping: Shipping;
    private items: Array<OrderItem>;
}
类顺序扩展了AggregateRoot{
私有clientId:DomainID;//或clientId,非必需
私人航运:航运;
私有项:数组;
}
只有在需要其他客户机属性的缓存副本来维护订单本身的完整性时,才会将它们拉入

clientId顺序为您提供了在需要时获取客户机数据副本所需的钩子。钩子通常是通过拥有一个域服务来实现的,该服务了解如何从客户机id中找到所需数据的副本。

通常的答案是,您通过id引用客户机,而不是缓存其属性

class Order extends AggregateRoot {
    private clientId: DomainID; // or ClientID, not essential
    private shipping: Shipping;
    private items: Array<OrderItem>;
}
类顺序扩展了AggregateRoot{
私有clientId:DomainID;//或clientId,非必需
私人航运:航运;
私有项:数组;
}
只有在需要其他客户机属性的缓存副本来维护订单本身的完整性时,才会将它们拉入


clientId顺序为您提供了在需要时获取客户机数据副本所需的钩子。钩子通常是通过域服务来实现的,域服务了解如何从客户端id中找到所需数据的副本。

另一种方法是,您还可以按顺序保留客户端字段的副本

你可能会问为什么

因为订单(业务需求可能会有所不同)是在单个时间点发生的。如果订单在给定时间点发生在
CustomerId:1
上,则此时该客户的
姓名为:John Doe
联系人电话为:555 abc xyz
。这才是真正重要的(同样:业务需求可能会有所不同,但是,请与您的领域专家交谈)


如果客户更改其
姓名
联系人电话
,您可以(或不可以-取决于使用情况)为客户的待定的订单更新,但不要为已完成的订单更改(因为为几周或几年前发生的订单更新电话号码没有意义).

另一种方法是,您还可以按顺序保留客户端字段的副本

你可能会问为什么

因为订单(业务需求可能会有所不同)是在单个时间点发生的。如果订单在给定时间点发生在
CustomerId:1
上,则此时该客户的
姓名为:John Doe
联系人电话为:555 abc xyz
。这才是真正重要的(同样:业务需求可能会有所不同,但是,请与您的领域专家交谈)


如果客户更改其
姓名
联系人电话
,您可以(或不可以-取决于使用情况)为客户的待定的订单更新,但不要为已完成的订单更改(因为为几周或几年前发生的订单更新电话号码没有意义).

谢谢。我开始写一个包含额外信息的大编辑,在写的过程中我意识到了很多事情。比如说,
Client
ClientID
实际上可能是不相关的(想象一下为其他人订购礼物)。我将把
clientId
放入
Order
聚合中,并将
receiverClient
作为值对象。(并与商界人士讨论命名问题)谢谢。我开始写一个包含额外信息的大编辑,在写的过程中我意识到了很多事情。比如说,
Client
ClientID
实际上可能是不相关的(想象一下为其他人订购礼物)。我将把
clientId
放入
Order
聚合中,并将
receiverClient
作为值对象。(并与商界人士讨论命名)它不是一个实体,你的想法是正确的。这只是订单创建时记录的一些细节。它不是一个实体,你的想法是正确的。这只是订单创建时记录的一些细节。