值对象和实体之间的实际区别。 DDD

Actual difference between Value Objects and Entities. DDD

领域驱动设计,我试图找出某种数学上而非直观上的区别。在我当前的项目中,我们在 banks 之间进行了某种 payment 的传输。 在这个系统中,银行不像实体建筑那样承载,而是抽象的 bank。它肯定有 BankCodecountryBank Name。问题是:Is the bank should be an entity or value object。直觉上觉得应该是entity,但是有人说可以像value object

据我所知,entities可能具有相同的属性,但仍然不同。另一方面,value objects 不能有 identity,它们应该相同,以防所有属性都相同,它们应该回答它们是什么,而不是它们是谁或它们是什么。如果我错了请纠正我。

在系统中,banks 在任何流程中都不会发生变化,就像另一个值对象 CountryCurrency,可能我们可以说,具有相同银行代码的银行,名称和国家是同一家银行。以及我们可以说 Country 与相同的国家代码(ISO 格式)是相同的。但是,对我来说,我仍然觉得它们是实体。有人可以证明我哪里错了,并给出数学证明它们应该是 Value 对象或 Entities.

目前,我最终得到的是:“The one difference between Entity and Value Object is the entity can contain the all same attributes and still be different, the Value Object can't”,比如交易和人可以拥有相同的金额和货物,相同的名字,但仍然不同,如果国家/地区,地址应该相同、城市和门牌号相同。请大家指正,或许还有更多不同之处

在您的情况下,bank 应该是一个 实体 ,因为每个银行都有唯一的 BankCode 来标识它。即使此时您的系统不更新银行,也不意味着以后就不可能了。

如果银行的某些属性发生变化,例如,它的主要办公室地址(只是编造的...),无论您得到什么变化,唯一标识它的代码将保持不变,因此相同银行,但主要办公地址不同。

这个银行 BankCode: 1234, Location: US TexasLocation 属性改变后的这个银行 BankCode: 1234, Location: US Colorado 还是一样。

给出数学定义很困难,因为数学是关于 .

这种独特性基于不是基于属性而是基于某种身份保持不变,因为它更接近现实世界的运作方式,因此很难给出非直观的定义。

这是数学中 ValueObject 的一个很好的例子。假设您使用向量和矩阵为 3D 数学库建模。 VectorMatrixValueObjects

假设我们有两个向量:

  • v1 -> x = 1,y = 2,z = 3
  • v2 -> x = 1,y = 2,z = 3

您确实有两个向量,但从数学的角度来看,它们是相同的,即代表相同的事物并且相等。这同样适用于矩阵。

让我们来个转换T(比如翻译)。在数学中你有:

v3 = T (v1, v2)

应用此转换后,您将获得一个全新的矢量。我们矢量的这个品牌可能与 v1 和 v2 具有相同的坐标,因此它们将相等,但是您不要更改 v1 或 v2 .

假设你剪了头发,假设这也是一种转变。如果这在现实世界中是真的?

You2 = T(理发师,You1)

不,不是。还是换了发型的你,变身后的你并没有变成不一样的你

建模时的一个很好的例子是概念何时可以是值和实体,具体取决于系统。Money。在您进行银行转账的情况下,您是在账户之间转账。在这种情况下,Money 是一个 ValueObject 因为你不区分 5$ 和 5$,它仍然是相同的 amount .

假设你去一家商店买一杯饮料,这杯饮料是 5 美元。如果你有两个 Banknotes 的 5 美元,你用哪个来支付并不重要,因为它们代表相同的 金额 .

另一方面,每个Banknote都有一个序列号。两个5$Banknotes会有不同的序列号,使它们独一无二,不同,所以它们是Entities.

如果您的系统跟踪 Banknotes,那么您将其建模为 Entity。如果不是,则将其建模为 ValueObject.

向量和矩阵是否有序列号或任何类型的标识?你的车(如果你有的话)会。如果你去一个有很多车的停车场,你有两辆你的车和别人的车,它们完全一样,你在乎你开哪一辆吗?好吧,还是拿你的吧,不然就是偷东西,犯法,别人不高兴。

让我们再次使用向量和变换 T。

T(v1, v2)、T(v2, v2) 和 T(v1, v1) 都给出相同的结果,因为这两个向量是相等的,我们不关心我们使用哪个。

Is the bank should be an entity or value object

首先要问的问题是:您需要为银行建模吗?

特别是,您是否控制银行更改的规则?或者您只是在管理由其他机构提供给您的本地 cache/repository 信息(与 ISO 3166 维护机构向我们提供国家代码信息的方式相同)。

域驱动设计中的一个重要原则是注意确保您正在对应该建模的事物进行建模。

we could say, that banks with the same bank code, name, and country are the same banks. As well as we can say that Country with the same country code (ISO format) is the same. But still, for me, I feel like they are entities.

是的。银行当然是实体。国家也是如此。

但它们是真实世界的实体。您的域模型不拥有它们。您在数据存储中拥有的是有关这些实体的本地缓存信息。

例如,ISO-3166-1标准是一个动态文档;它最近更改为 2020-03-02(对 MK 全名的更正)。一个给定的国家代码 和日期 给你一个固定的标识符。因为国家代码保留 50 年,所以国家代码本身对于重要的时间段是明确的。

令牌MK当然是一个值对象。表示令牌 MK 的查找 table 表示 North Macedonia 是一个实体。但是...它是一个 stable 实体,因此在某些情况下我们可以将其视为固定的。

(这是通用的 属性 标准化标识符 - 如果不采用标准就毫无价值,因此变更管理政策旨在让采用者更轻松)。

不过,除非您自己为 ISO 3166/MA 工作,否则它不是您的实体。您所掌握的信息只是一份副本。

我猜你的银行代码也是如此。

领域驱动设计的部分要点:如果我们努力确保我们真正理解我们试图建模的实际业务问题,那么我们的后续实施将与我们想要的保持一致, 并且在面对新需求时易于更改。

也就是说,您确实需要确保您理解 "bank" 在您的域上下文中以确定它是实体还是值对象。

两者之间的经典示例差异基于上下文(正如 DDD 始终如此),所以让我们开始吧...

经典示例:飞机航班(分配座位或任何座位)...

实体

每个座位都有编号,你给一个乘客分配一个特定的座位(座位:10 E)

值对象

飞机有多个座位,您在飞机上给乘客 1 个(或更多)座位(不指定,随便坐)。

关键问题是,您关心的是单个实体(一个席位)还是只关心可用席位的总数?

你可以在这里看到 DDD - 上下文分离,即使你分配座位,也不是每个上下文都会关心。如果我在做容量块,我不关心实体,我只想知道

var unused = available - used.

您的初始问题

简单回答

它是一个实体

更深入的回答

这取决于上下文。

然后...

在某些上下文中,它本身可能是一个领域模型,就像您的上下文一样。

如果我想从 Bank1 和 Bank2 转移资金,我会做...


// Both domain models
Bank sendingBank = _bankStore.Get(fromBank.Id);
Bank receivingBank = _bankStore.Get(toBank.Id);

_moneyTransfer(sendingBank, receivingBank, amount);

或者

调用代码

Customer customer = _customerStore.Get(customerId);

var balanace = customer.GetBalance();

实体版本:客户

public class Customer {
    public Bank {get; private set;}

    public Decimal GetBalance () {
       return this.Bank.GetBalance();
    }
}

值对象版本:客户

public class Customer {
   public Balance {get; private set;}

   public Customer (..., decimal bankBalance) {
      this.Balance = bankBalance;
   }
}

也许,我可以从另一个方面来解释这个问题。实体通常有一个身份,但是这个身份用来做什么呢?我认为它用于跟踪实体状态的变化。为什么要跟踪状态变化?因为该实体被其他 context/object 根据身份引用。

所以说要看具体context/case。对于某些上下文,我们不关心状态的变化,它将是值对象,否则它将是实体。

地址通常被视为值对象。您在购物网站上输入了您的地址,您的家人登录同一系统时不会参考该地址,即使你们所有人都有相同的地址。

但是当你浏览、修改或删除之前输入的地址时,它必须是一个实体,因为它是由地址编辑器和地址簿引用的,并带有标识。

当订单使用地址时,它将是一个对象值或实体,具体取决于地址簿更改时应更改的地址。