【问题标题】:Microservice Clients Circular Dependency微服务客户端循环依赖
【发布时间】:2021-01-04 14:52:23
【问题描述】:

在微服务架构中,使用客户端包在服务之间进行通信,我们遇到了两个客户端包相互依赖的问题,从而产生了循环依赖。

我们正在尝试找出最佳解决方案,我想知道是否有人能够帮助或为我们指明正确的方向。

这是场景:

  • 两种服务,汽车和保险
  • 两个客户端包,CarClient 和 InsuranceClient。

当任何服务需要与 Car 服务通信时,它应该使用 CarClient 包来完成。并且每当任何服务需要与 Insurance 服务进行通信时,它都应该使用 InsuranceClient 包。

CarClient 包有一个数据传输对象 (DTO) Car,其中一个属性是 insurance。此属性的类型是 InsuranceClient 包中可用的 DTO,CarInsurance

问题在于CarInsurance DTO 需要访问 CarClient 包中可用的枚举 CarTypeEnum。现在我们有了两个相互依赖的包。

我能想到的可能解决方案:

  1. 这是由于设计不当造成的。重新设计服务和包以防止这种循环依赖。
  2. 将枚举移动到单独的包中,因此,两个客户端都可以依赖这些包,但客户端不会相互依赖。

感谢任何帮助。

【问题讨论】:

  • 为什么汽车和保险是两个独立的客户/服务?你能把两者合并吗?我只是一个新手,我不知道您的应用程序的详细信息,但是如果这两个服务已经相互依赖,为什么不把所有东西都放在一个屋檐下呢?
  • 保险服务执行多项与汽车无关的任务,包括其他类型的保险(例如:家庭保险)。微服务的想法是将这些任务解耦到它们自己的存储库(服务)中,这样我们就可以更有效地编码和部署。然而,这是有代价的,因为它确实增加了一些复杂性。
  • 您是在谈论 Java 意义上的 package 吗?因为那为什么在这种情况下循环依赖会成为问题?
  • 您是否想让您的服务工作过度?诸如注册汽车并在一次通话中为其添加保险计划之类的事情?那么可能你想把两个责任分开,一个是注册汽车,然后添加保险计划,这只是一个例子,如果你能解释一下情况,我可以给一些更好的主意。

标签: php node.js microservices


【解决方案1】:

您不应该在服务之间共享任何代码,因为这违背了它们 100% 独立的整个目的。

在 MS 架构中,CarDTO 将仅具有与汽车相关的属性。如果您需要有关保险的信息,需要单独调用保险服务以获取仅包含保险财产的 InsuranceDTO。

当调用任一服务时,您会使用一些键将它们绑定在一起。 IE。您将使用从客户服务获得的 customerId 调用汽车服务,并且您的 CarDTO 将具有 carId,然后您可以使用 customerId/carId 调用保险服务以获取 InsuranceDTO。

【讨论】:

  • 我们正在获取保险服务以获取保险,但我们正在将该对象添加到 Car dto 中的属性中。这就是我们需要类型的原因。但是假设我们不这样做,我们如何处理枚举问题?保险服务需要知道它需要为哪种类型的汽车创建保险,因此,它会收到带有有效负载的汽车类型 ID。这就是 dto 具有该属性的原因。由于 dto 存在于保险客户端中,因此该客户端依赖于汽车客户端来访问汽车类型枚举。如果我们不这样做,我们将需要在这些 repos 之间复制枚举。
  • @crabbly 这正是您遇到问题的原因。您正在尝试将这两个服务紧密耦合。 DTO 应该是完全独立的。想象一下,如果您更改一个共享类并引入一个重大更改——现在您需要重建所有使用它的东西并重新部署使用它的所有东西,您可能会遗漏一些东西。想象一下,如果您有 1000 项服务。更改共享类将意味着重新部署所有内容。
  • @crabbly 在 MS 中,您应该能够部署单个服务而不会破坏任何东西,或者必须重新部署除该服务之外的任何东西。 MS 中的重大更改是“不允许的”,您需要在设计服务时考虑到“版本控制”。例如,我们在我们的上使用内容类型协商,它工作得很好,虽然它有点复杂,但要正确。在一天结束时,/cars/v1 将拥有您当前的版本,而 /v2 将拥有具有重大更改的版本,并且服务可以根据需要迁移到 v2。
  • P.S.可以在 MS 中“复制和粘贴”DTO。毕竟只是json。无论如何,您可能都不想与客户端 DTO 共享服务器 DTO,因为它们在现实世界中可能会有所不同(注释等)。
  • 当我们更改 DTO 时,我们正在更改 Api 合约,因此,无论如何我们都应该预料到事情会中断。这就是我们进行版本控制的原因。为了防止这种情况发生。因此,这对我们来说不是问题,并且与上述问题无关。问题实际上只是试图重用那些 DTO 和枚举。所以在你看来,我们应该在每个 repo 上复制它们。我想这是一种方法,并且确实可以解决依赖关系。
【解决方案2】:

我阅读了先前答案的 cmets 中的长对话,我想补充一点,以支持“在每个 repo 上复制它们”的想法。

如果您基于 DDD(领域驱动设计)的理念设计服务,您可能会意识到相同的概念/实体在不同领域可能意味着不同的东西。

这意味着 CarService 中的“保险”可能具有 - 根据域/要求 - 与 InsuranceService 中的属性完全不同的一些属性。这就是为什么 CarService 中的保险概念应该有自己的 dto,它应该完全不受 InsuranceService 的保险定义的影响。

【讨论】:

  • 是的,在某些情况下,这似乎是正确的方法。特别是枚举。在其他情况下,我们可能应该消除客户端级别的依赖,让服务在内部处理关系。
【解决方案3】:

您不能在保险服务中使用 CarTypeEnum。只需使用 CarId。当您需要知道类型时,只需向汽车服务部门询问该信息即可。

【讨论】:

  • 枚举在将汽车类型用于业务逻辑时很有用。与其让代码对静态数字做出反应,不如使用枚举来代替。
猜你喜欢
  • 1970-01-01
  • 2021-04-16
  • 2015-09-03
  • 2021-08-15
  • 2020-05-21
  • 2022-01-08
  • 2021-05-12
  • 2020-12-09
  • 2017-09-08
相关资源
最近更新 更多