是否应该使用数据库主键来识别跨微服务的实体?

Posted

技术标签:

【中文标题】是否应该使用数据库主键来识别跨微服务的实体?【英文标题】:Should database primary keys be used to identify entities across microservices? 【发布时间】:2021-03-23 01:42:10 【问题描述】:

假设我有两个微服务:服务 A 和服务 B。

服务 A 拥有完整的客户数据,而服务 B 需要该数据的一小部分(它通过一些批量加载从服务 A 获取)。

两种服务都将客户存储在自己的数据库中。

如果服务 B 然后需要与服务 A 交互以获取额外的数据(例如 GET /customers/id),它显然需要在两个服务之间共享的唯一标识符。

因为 id 是 GUID,所以在服务 B 中创建记录时,我可以简单地使用服务 A 中的 PK。所以两个 PK 匹配。

然而,这听起来非常脆弱。一种选择是将“外部 ID”(或“源 ID”)作为单独的字段存储在服务 B 中,并使用它与服务 A 交互。这可能是一个字符串,因为有一天它可能不是 GUID。

这方面有“最佳实践”吗?

更新

所以我做了更多的研究,发现了一些相关的讨论:

Should you expose a primary key in REST API URLs?

Is it a bad practice to expose the database ID to the client in your REST API?

Slugs as Primary Keys

结论

我认为我试图在服务 A 和 B 中保持客户主键相同的想法是错误的。这是因为:

    显然 PK 是特定于服务实现的,因此它们可能完全不兼容,例如UUID 与自动递增的 INT。 即使您可以保证兼容性,尽管这两个实体恰好都称为“客户”,但它们实际上是两个(可能非常不同)的概念,并且服务 A 和服务 B 都“拥有”自己的“客户”记录。您可能想要做的是跨这些服务同步一些客户数据。

所以我现在认为任何一项服务都可以通过其自己的唯一 ID(在我的情况下为 PK GUID)公开客户数据,如果一项服务需要从另一项服务获取额外的客户数据,它必须存储另一项服务标识符/密钥和使用它。所以基本上回到我的“外部 id”或“源 id”的想法,但也许更具体为“服务 B id”。

【问题讨论】:

它并不脆弱,但需要。即使您使用外部 id,该值是否仍会存储在服务 A 中以匹配来自服务 B 的请求? 是的,我当然需要外部 ID,但我的问题是是否在这两个服务中都使用这些作为数据库主键。我的建议是否定的,因为这依赖于 a) 任何一项服务的 PK 都不会改变(可能并不总是得到保证)和 b) 客户的唯一标识符可能是电子邮件地址,因此不能用作数据库PK。所以我在这里真的回答了我自己的问题。我真的只是希望在某个地方能有一篇博客或文章来验证这一点。 好吧,我想这是我们可以遵循的很酷的方法 你为什么首先需要它?如果它是客户,它可能具有公司内部使用的客户 ID(即放在发票上或在内部引用客户)。这是您的独特价值,您也可以使用它来调用其他服务。 GUID、自动增量等“主”键只是用于建模关系的 RDBM 工具,在基于文档的数据库中可能不存在。想象一个订单和订单位置实体,对于 RDBMS,您需要一个自动增量列来建模关系(即将位置分配给订单)。 仓位也可能有仓位编号,通常是向上计数,每次新订单从1开始,但这不适合FK关系。现在,当您拥有文档数据库时,根本不需要 FK,因为关系由文档结构决定,即 "orderNumber": "O12345", "address": ..., "positions": [ "positionNumber":1, "description": "Some Product", "priceNet": 10.00, ...] 【参考方案1】:

好吧,如果您对值对象有一个想法,那么业务 ID 会更适合设计。

DDD专注于业务,纯UUID/Auto increment ID无法呈现。

使用业务含义 ID(UL ID),例如客户 ID,而不是简单的 ID。

【讨论】:

【参考方案2】:

我认为这在一定程度上取决于数据源和您的设计。但是,我会避免共享的一件事是主键,它是外部服务的 GUID 或自动增量整数。这些是您的服务的内部细节,而不是其他服务应该依赖的内容。

我宁愿有一个外部 ID,它更容易被其他服务甚至整个业务所理解。它可以是唯一的客户编号、订单编号或保单编号,而不是 id。您也可以将它们视为“企业 ID”。还要记住的一件事是,外部 id 也可以暴露给最终用户。因此,这是一种在整个组织和服务中识别“实体”的普遍方式,无论您是否具有事件驱动设计或您的服务是否通过 API 进行通信。我只会将数据库 ID 公开给基础设施或存储库。除此之外,它只是一个企业/外部 ID。

【讨论】:

以上是关于是否应该使用数据库主键来识别跨微服务的实体?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Django REST 框架跨微服务共享数据库关系

跨微服务传递用户信息

跨微服务发送公共 API 版本控制

跨微服务环境模块化 Terraform IaC

如何将跨微服务的端到端测试包含到多个持续交付管道中?

如何通过本地化事件正确实现微服务内部强一致性,事件总线跨微服务间最终一致性