“实体”几乎是每个开发人员耳熟能详的概念。特别是对于使用过 ORM 框架的开发人员,当你提到“实体”,他们可能马上就会想到“就是需要映射为数据表(Table)的那些对象嘛”。
实体也是领域驱动设计(DDD)的战术层面最重要的概念之一(个人以为,DDD 战术层面最重要的三个概念是聚合、实体以及值对象)。由于大家对“实体”如此熟悉,所以要理解下面的讨论其实并不需要了解 DDD。
即使是不了解 DDD 的开发人员都“公认”实体是指这一类对象:拥有标识符(简称 ID),不管对象的状态如何变化,它的 ID 总是不变的。我们可以把这个 ID 称为实体的领域 ID。
比如说,我们的银行账户(Account),总是有一个编号(账号)的。我们存钱、取钱,账户里面的钱会发生变化,但是账号不变。我们通过这个账号,能够查询到账户的余额。所以,我们可以把银行账户建模为一个实体,选择它的编号作为这个实体的 ID。
问题
我相信很多人都思考过类似的问题:
实体的领域 ID 是不是应该映射到关系模型(数据库)的自然键?如果一个实体的 ID 已经是“自然键”了,那么与之对应的关系数据库的表(Table) 中还有必要再引入这个 ID 之外的代理键吗?
还有,如果领域 ID 可以是代理键,那么它什么时候应该是代理键?
我们可能都见过这样的软件系统:开发人员会不分青红皂白地给每个表(实体)设计一个代理主键。那么,在代码中这样重度地使用代理键是合理的做法吗?
如果一个实体需要被其他实体引用的时候,其他实体是不是应该尽可能统一地通过持有它的领域 ID 的值来引用(指向)它?
自然键与代理键
在上面的问题中,出现了自然键和代理键的概念。考虑到代理键是和自然键是相对的概念,我们先搞清楚什么是自然键(Natural Key)就可以了。那么,什么是自然键?根据维基百科:
In relational model database design, a natural key is a key that is formed of attributes that already exist in the real world. For example, a USA citizen's social security number could be used as a natural key.
In other words, a natural key is a candidate key that has a logical relationship to the attributes within that row. A natural key is sometimes called domain key.
中文翻译过来就是:
在关系模型数据库设计中,自然键是指由现实世界中已经存在的属性所构成的“键”。举个例子,美国公民的社会保险号码可以用作自然键。换句话来说,自然键是和那一“行”中的属性存在逻辑关系的候选键。自然键有时候也被称为领域键。
根据这个定义,自然键有时候也被称为领域键——领域键和领域 ID,啊哈,称呼已经很接近了不是吗?
我们看到,这个定义还依赖于另外一个概念:现实世界(the real world)。那么,什么是现实世界?如果这个概念没有定义,那么自然键的概念还是“不清不楚”的。
提示
虽然在讨论