实体标识与数据库主键


今天,我们将讨论DDD意义上的标识与数据库主键之间的区别。 我们经常将两者混合在一起,但它们真的是一回事吗?
实体标识
在DDD的背景下,标识是实体固有的东西。 只有实体拥有它; 它是用于区别于所有其他实体的唯一标识它们自己的东西。
例如,我们认为两个人不同,不管他们拥有什么“属性”。 纯粹巧合的是,它们可能具有相同的名称,年龄,地址等。因此,我们仍然不会将它们视为同一个人。 每个人都有一种固有的标识,可以将他们与其他人区别开来。
但究竟是什么呢?
问题是,你无法确定它。 你无法分辨出某人的标识。 这是设计的结果。 标识概念是: 每个标识必须是不可变的和全球唯一的 。
这就是人类如何用它来跟踪我们需要跟踪的对象的操作方式。 我们为每个事物分配一个无形的唯一标签,然后使用该标签标识该对象。
这也是你跟踪人们的方式。 一个人可以进行巨大的转变(例如,从一个孩子成长为一个完全成熟的成年人),你仍然知道它是同一个人。 那是因为当你第一次见到那个人时,你会给他们一个独特的标识,即使在他自己彻底改变之后也不会改变。
由于这两个特征 - 不变性和唯一性 - 您无法真正使用任何实体的自然属性作为其标识。 这些属性往往会随着时间而改变。 它们往往也不是唯一的。
想用这个人的全名作为他们的标识吗? 不,名字不是唯一的。 社会安全号码(SSN)?不,那些可以改变。
当然,在领域建模方面,您需要记住任何模型都只是 - 模型。 只是现实世界的简化表示,复杂恰到好处。 您无需在模型中反映问题域的完整复杂性。
因此,可能在某个特定的领域模型中,使用人的SSN(或电子邮件)作为其标识就可以了。但那些往往是非常简单的模型。 如果你处理任何或多或少复杂的事情,你将不可避免地遇到试图调和所谓的不可变标识的变化的问题。 所有复杂系统都经历了这样的变化。
因此,一般而言,使用自然属性(来自现实世界的属性)并不是表示实体标识的好方法。它必须是无形的东西; 你人为地创建并分配给实体的东西,因此可以确保它既独特又不可变。

数据库主键
数据库主键怎么样? 它与标识的概念有什么关系?
它是一个完全独立的概念,与DDD或域建模无关。

但事情就是这样。 你需要以某种方式持久保存你的实体。 不仅保存,而且以后还原它们并能够跟踪他们的标识。 这意味着在此持续恢复周期期间,标识不得更改。

什么数据库功能可以让你做到最好? 那是对的,主键。
事实证明,数据库主键与领域驱动设计中标识概念的很近似。 数据库提供持久性,允许您确保在持久化后标识不会更改。 主键特别有助于实现系统中所有标识的唯一性。 行的表名加上其主键可以很好地实现标识概念。
您可能听说过在设计数据库时从不使用自然主键的准则。 Martin Fowler在他的企业应用程序架构模式中写到了这一点。 现在你知道了这个指南的来源。
自然主键不适合精确地表示标识,因为它们往往会随着时间的推移而改变(如果只是错误的话),并且可能并不是那么独特。 代理主键可以更好地处理这些问题。 您可以在创建记录时分配一次,然后在修改电子邮件或SSN等基本属性后保持不可变。 它也很容易确保它们的独特性,因为这些键应该看起来没有外部约束。

值对象和标识
我们在这里也提到值对象。 现在,我听到你说:值对象没有标识? 这是真的,但还有另一种方法可以看到这一点: 值对象的标识跨越其所有属性 。
但是不存在矛盾吗? 难道我没有告诉你自然属性不能成为一个好标识吗?
这里没有矛盾,因为我们不需要跟踪值对象。 它们是不变的。 只要您需要在值对象中引入更改,就可以创建一个新对象并用它替换旧的对象。
因为值对象是不可变的,所以我们自动满足标识的第一个要求:不可变immutability。

独特性唯一性怎么样? 我们如何实现它?
好吧,请记住值对象是可以互换的。 只要属性匹配,我们就可以自由地替换另一个。 还有另一种方法可以查看:如果两个值对象可以互换,则意味着它们是相同的。 他们有相同的标识。 否则,你将无法使用一个代替另一个(正如你不能使用一个约翰代替另一个约翰,除非他们是同一个人)。
因此,每个值对象的定义都是唯一的。 这给了我们标识的另一个组成部分。

因此: Value Object ==属性== 标识Identity

实体标识与数据库主键:总结

  • 实体的标识是无形的
  • 标识是不可变的,全球唯一的
  • 不要将自然属性用作实体的标识。 它们随着时间而变化,它们可能不是唯一的
  • 数据库主键很接近标识的概念
  • 不要使用自然主键。 代理主键最适合实体的标识
  • 值对象的属性是它自己的标识