使用 Natural key 作为 DomainObject 或 GUID 的 ID + 自增领域驱动设计
using Natural key as the ID of DomainObject or GUID + auto-increment Domain Driven Design
我已经阅读了很多关于 DDD 的文章,并且注意到大多数在持久化到数据库时都使用 GUID 作为它们的 ID。他们说 GUID 可以很好地扩展,而自动递增 ID 在可扩展性方面是一个很大的禁忌。
我现在很困惑是使用 GUID
还是 auto-increment
。
基本上域名大约是 membership system (binary tree). (tracking of register members)
第一个要求是我们应该有一些在系统中唯一标识它们的东西(我们称之为Account No.
)也许7digit。
然后新的Members
可以被另一个Member
注册。我们称之为推荐。
现在我打算做的是将 MemberId
的 GUID 类型作为 DomainObject Id 用作主键,用于连接、外键(在推荐时,referer_id 将是 GUID MemberId
)。 AccountNo
将是一个自增列,也可能会通过 MAX() + 1 从存储库中获取。主要用于系统和链接中的搜索功能。
DomainObject 的 ID 是否应该对系统用户隐藏,因为它只是一种技术实现?
两者结合可以吗?数据库中的 GUID 为 row_id(代理键)。和(自然键)的自动递增?
是否可以从构造函数中排除 AccountNo
因为它无论如何都会自动递增?强制不变量的需要又如何呢?那么从存储库中获取下一个 ID 并在构造函数中包含 AccountNo
的方法是什么?
我是否应该坚持使用自动递增 ID 而忘记 GUID,删除 MemberId
并让 AccountNo
成为 DomainObject 的 ID?
注意:
我不是在构建下一个 facebook 之类的东西。
我只是想练习 DDD 的战术方面,学习如何在了解优缺点的情况下做出艰难的架构决策。
我只是想练习 DDD 的战略方面,以学习如何在了解其优缺点及其实施的情况下做出艰难的架构决策。
如果我们将通过会员注册制作 3 个场景:
- 第一种情况:会员注册每分钟进行一次。
- 第二种情况:会员注册每小时进行一次。
- 第三种情况:会员注册每天最多5次。
它将如何影响决策?
技术栈:
- ASPMVC 5
- Sql 服务器 2014
- C#
- 小巧的 ORM
你的问题可能太多了,无法给你一个完整的答案,因为ID设计并不简单,有很多方面。我可以推荐 Vaughn Vernon 的书 "Implementing DDD",它有一节专门介绍身份设计(第 5 章 "Entities" - 唯一身份)。
无论如何,我会尝试为您指明正确的方向,而不是背诵该章中的所有内容:-)。
你需要什么?
您已经提出了一些关于 ID 设计的问题,但您还有更多问题需要提出。只有这样,您才能决定 GUID、DB 生成的 ID 或仍然不同的 ID 是否合适。
- ID 的域意义是什么?可能 ID 只是为了促进技术解决方案,但根本不是域的一部分?
- ID是谁提供的?这可能是用户、应用程序、数据库甚至是另一个限界上下文。
- 你什么时候需要身份证?在创建关联实体之前、创建时或仅在实体持久化时?
这些问题的答案将限制您可以使用的 ID 生成类型。
你应该知道什么
关于 ID 设计有一些规则。强烈推荐关注他们,以免以后搬起石头砸自己的脚:
- 确保您的 ID 是唯一的。对于用户提供的 ID,这可能尤其令人担忧。您需要强制执行唯一性并为您的用户提供检测现有 ID 的可能性。对于应用程序生成的随机 ID 或数据库生成的 ID,这通常不是问题。
- 确保您的 ID 稳定。永远不要更改实体的 ID!毕竟,ID 是您用来引用实体的东西。这样做的结果是您不应该将可能更改的内容作为您 ID 的一部分。例如。一个人的姓氏可能不是一个好的选择,因为当某人结婚时它可能会改变(并且由于非唯一性也可能有问题)。
- 如果 ID 只是一个技术概念,请隐藏它们。从 DDD 的角度来看,将技术概念作为领域模型的一部分是错误的。
例子
以下是如何找到 ID 设计的示例:
允许延迟创建 ID(即通过持久性)意味着您在创建实体时确实拥有没有 ID 的实体。因此,如果您需要早期或即时 ID,则不能使用数据库生成的 ID(除非您同意联系数据库只是为了获取 ID)。
然后您可能会确定用户对 ID 不感兴趣,因此必须指定 ID 会很奇怪。这留下了应用程序生成的 ID。
对于应用程序生成的 ID,您需要确保 ID 是唯一的。这对于单实例应用程序来说是微不足道的,但一旦您创建具有多个应用程序实例的负载平衡设置时,问题可能会更多。这就是为什么很多人一开始就使用随机ID(比如GUID)的原因,这样在scale out的时候就不会钻牛角尖了。
如您所见,这个例子做了很多假设。没有对错之分,但有了上述问题,您应该能够做出明智的决定。
I just want to practice the Tactical side of DDD to learn how to make hard architectural decisions knowing their PROS and CONS.
你错了。你不能通过战术来学习战略。战术是实施战略的方法。但是你首先需要一个策略。
不管怎样,关于你的问题,很简单:使用 Guid。它有2个优点
- 全局标识符
- 可以从应用程序轻松生成。自动递增的 id 意味着复杂的服务或对数据库的依赖。不要让你的生活复杂化。
也应该使用自然id,比如AccountNo。但是,Guid 用于技术目的。自然键格式将来可能会改变,Guid 可以很容易地支持自然键多种格式。
作为一种实践,您的实体 ID 最好是一个值对象(即使它只是 Guid)。您也可以将 guid 合并到 AccountNo
中,一个 VO 不必只有一个值。例如,在我当前的应用程序中,我有一个 ProjectId(Guid organization,Guid idValue)
和 ProjectAssetId(Guid organization,Guid projectId,Guid idValue)
。
They say that GUID scales well and auto incrementing ID's are a big no-no when it comes to scalability.
自动递增整数的主要可伸缩性问题在于插入:由于新值 "bunch up" 一起位于值范围的上限,它们往往会达到相同的 "side" B 树(可能是同一个叶节点),导致锁定并降低并发性。
每分钟插入一次,您根本看不到任何这些,因此请根据其他条件选择您的密钥。正如您所描述的那样,自动递增整数在您的场景中会很好地服务......它们更轻量级并且可能比 GUID 表现更好。如果 32 位变体不够宽,就使用 64 位。
顺便说一句,自动递增整数不是通过查询 MAX() + 1
生成的 - 所有 DBMS 都有自己的高性能版本 "sequence generator",您可以直接使用。
您还可以 return 将生成的值直接发送给客户端,而无需额外的往返(例如 Oracle 的 RETURNING 或 SQL 服务器的 OUTPUT 子句)。不幸的是,ORM 并不总是能很好地处理...
请允许我为自增的想法辩护。的确,GUID 更具可扩展性。但是任何设计师在某些时候都应该问的一个重要问题是 "How much scalability do I need?"
答案很少"As much as possible!"在现实世界中,任何事物都是有限度的。我们的数据库模拟真实世界。
例如,如果您正在与人(用户、客户、学生等)打交道,一个 64 位整数可以包含地球上所有人口的许多倍。很多次。我们在这里谈论 "population of the galactic empire"。使用 bigint,您几乎可以唯一地识别宇宙中的每个原子。
不要偷懒,尤其是在设计阶段。设计一个合理的安全边际,然后继续。任何更多的东西都会不必要地增加系统的复杂性和 "friction"。
我在这个领域的经验现在可以用几十年来衡量——其中有好几年。在那段时间里,我从来没有使用 GUID 来实现可伸缩性。我发现 GUID 的唯一实际用途是在不同的(通常是远程的)数据库中创建实体,然后必须将这些数据库合并到一个中央数据库中。 GUID 消除了(从统计学上讲,即)合并期间发生冲突的可能性。
我已经阅读了很多关于 DDD 的文章,并且注意到大多数在持久化到数据库时都使用 GUID 作为它们的 ID。他们说 GUID 可以很好地扩展,而自动递增 ID 在可扩展性方面是一个很大的禁忌。
我现在很困惑是使用 GUID
还是 auto-increment
。
基本上域名大约是 membership system (binary tree). (tracking of register members)
第一个要求是我们应该有一些在系统中唯一标识它们的东西(我们称之为Account No.
)也许7digit。
然后新的Members
可以被另一个Member
注册。我们称之为推荐。
现在我打算做的是将 MemberId
的 GUID 类型作为 DomainObject Id 用作主键,用于连接、外键(在推荐时,referer_id 将是 GUID MemberId
)。 AccountNo
将是一个自增列,也可能会通过 MAX() + 1 从存储库中获取。主要用于系统和链接中的搜索功能。
DomainObject 的 ID 是否应该对系统用户隐藏,因为它只是一种技术实现?
两者结合可以吗?数据库中的 GUID 为 row_id(代理键)。和(自然键)的自动递增?
是否可以从构造函数中排除 AccountNo
因为它无论如何都会自动递增?强制不变量的需要又如何呢?那么从存储库中获取下一个 ID 并在构造函数中包含 AccountNo
的方法是什么?
我是否应该坚持使用自动递增 ID 而忘记 GUID,删除 MemberId
并让 AccountNo
成为 DomainObject 的 ID?
注意:
我不是在构建下一个 facebook 之类的东西。
我只是想练习 DDD 的战术方面,学习如何在了解优缺点的情况下做出艰难的架构决策。
我只是想练习 DDD 的战略方面,以学习如何在了解其优缺点及其实施的情况下做出艰难的架构决策。
如果我们将通过会员注册制作 3 个场景:
- 第一种情况:会员注册每分钟进行一次。
- 第二种情况:会员注册每小时进行一次。
- 第三种情况:会员注册每天最多5次。
它将如何影响决策?
技术栈:
- ASPMVC 5
- Sql 服务器 2014
- C#
- 小巧的 ORM
你的问题可能太多了,无法给你一个完整的答案,因为ID设计并不简单,有很多方面。我可以推荐 Vaughn Vernon 的书 "Implementing DDD",它有一节专门介绍身份设计(第 5 章 "Entities" - 唯一身份)。
无论如何,我会尝试为您指明正确的方向,而不是背诵该章中的所有内容:-)。
你需要什么?
您已经提出了一些关于 ID 设计的问题,但您还有更多问题需要提出。只有这样,您才能决定 GUID、DB 生成的 ID 或仍然不同的 ID 是否合适。
- ID 的域意义是什么?可能 ID 只是为了促进技术解决方案,但根本不是域的一部分?
- ID是谁提供的?这可能是用户、应用程序、数据库甚至是另一个限界上下文。
- 你什么时候需要身份证?在创建关联实体之前、创建时或仅在实体持久化时?
这些问题的答案将限制您可以使用的 ID 生成类型。
你应该知道什么
关于 ID 设计有一些规则。强烈推荐关注他们,以免以后搬起石头砸自己的脚:
- 确保您的 ID 是唯一的。对于用户提供的 ID,这可能尤其令人担忧。您需要强制执行唯一性并为您的用户提供检测现有 ID 的可能性。对于应用程序生成的随机 ID 或数据库生成的 ID,这通常不是问题。
- 确保您的 ID 稳定。永远不要更改实体的 ID!毕竟,ID 是您用来引用实体的东西。这样做的结果是您不应该将可能更改的内容作为您 ID 的一部分。例如。一个人的姓氏可能不是一个好的选择,因为当某人结婚时它可能会改变(并且由于非唯一性也可能有问题)。
- 如果 ID 只是一个技术概念,请隐藏它们。从 DDD 的角度来看,将技术概念作为领域模型的一部分是错误的。
例子
以下是如何找到 ID 设计的示例:
允许延迟创建 ID(即通过持久性)意味着您在创建实体时确实拥有没有 ID 的实体。因此,如果您需要早期或即时 ID,则不能使用数据库生成的 ID(除非您同意联系数据库只是为了获取 ID)。
然后您可能会确定用户对 ID 不感兴趣,因此必须指定 ID 会很奇怪。这留下了应用程序生成的 ID。
对于应用程序生成的 ID,您需要确保 ID 是唯一的。这对于单实例应用程序来说是微不足道的,但一旦您创建具有多个应用程序实例的负载平衡设置时,问题可能会更多。这就是为什么很多人一开始就使用随机ID(比如GUID)的原因,这样在scale out的时候就不会钻牛角尖了。
如您所见,这个例子做了很多假设。没有对错之分,但有了上述问题,您应该能够做出明智的决定。
I just want to practice the Tactical side of DDD to learn how to make hard architectural decisions knowing their PROS and CONS.
你错了。你不能通过战术来学习战略。战术是实施战略的方法。但是你首先需要一个策略。
不管怎样,关于你的问题,很简单:使用 Guid。它有2个优点
- 全局标识符
- 可以从应用程序轻松生成。自动递增的 id 意味着复杂的服务或对数据库的依赖。不要让你的生活复杂化。
也应该使用自然id,比如AccountNo。但是,Guid 用于技术目的。自然键格式将来可能会改变,Guid 可以很容易地支持自然键多种格式。
作为一种实践,您的实体 ID 最好是一个值对象(即使它只是 Guid)。您也可以将 guid 合并到 AccountNo
中,一个 VO 不必只有一个值。例如,在我当前的应用程序中,我有一个 ProjectId(Guid organization,Guid idValue)
和 ProjectAssetId(Guid organization,Guid projectId,Guid idValue)
。
They say that GUID scales well and auto incrementing ID's are a big no-no when it comes to scalability.
自动递增整数的主要可伸缩性问题在于插入:由于新值 "bunch up" 一起位于值范围的上限,它们往往会达到相同的 "side" B 树(可能是同一个叶节点),导致锁定并降低并发性。
每分钟插入一次,您根本看不到任何这些,因此请根据其他条件选择您的密钥。正如您所描述的那样,自动递增整数在您的场景中会很好地服务......它们更轻量级并且可能比 GUID 表现更好。如果 32 位变体不够宽,就使用 64 位。
顺便说一句,自动递增整数不是通过查询 MAX() + 1
生成的 - 所有 DBMS 都有自己的高性能版本 "sequence generator",您可以直接使用。
您还可以 return 将生成的值直接发送给客户端,而无需额外的往返(例如 Oracle 的 RETURNING 或 SQL 服务器的 OUTPUT 子句)。不幸的是,ORM 并不总是能很好地处理...
请允许我为自增的想法辩护。的确,GUID 更具可扩展性。但是任何设计师在某些时候都应该问的一个重要问题是 "How much scalability do I need?"
答案很少"As much as possible!"在现实世界中,任何事物都是有限度的。我们的数据库模拟真实世界。
例如,如果您正在与人(用户、客户、学生等)打交道,一个 64 位整数可以包含地球上所有人口的许多倍。很多次。我们在这里谈论 "population of the galactic empire"。使用 bigint,您几乎可以唯一地识别宇宙中的每个原子。
不要偷懒,尤其是在设计阶段。设计一个合理的安全边际,然后继续。任何更多的东西都会不必要地增加系统的复杂性和 "friction"。
我在这个领域的经验现在可以用几十年来衡量——其中有好几年。在那段时间里,我从来没有使用 GUID 来实现可伸缩性。我发现 GUID 的唯一实际用途是在不同的(通常是远程的)数据库中创建实体,然后必须将这些数据库合并到一个中央数据库中。 GUID 消除了(从统计学上讲,即)合并期间发生冲突的可能性。