【问题标题】:Proper separation of concerns between microservices适当分离微服务之间的关注点
【发布时间】:2025-12-01 01:45:01
【问题描述】:

假设我想创建一个允许管理用户帐户的博客平台,因此我想出了 2 个微服务:

  • Blogging - 管理帖子、标签等
  • Users - 管理用户、他们的角色等。

我很清楚,Post 包含作者的 ID (User),User 包含他写的 Posts 的 ID。

问题是当客户端请求Post时,我还想返回作者的名字,并在DTO(视图模型)中一起发送给客户端。我看到了两种可能的解决方案:

  1. Blogging 服务的域中引入User(仅ID 和名称)的概念。每次客户端请求Post 时,所有相关数据都仅从该微服务中获取。每次在 Users 微服务中修改用户名时,都会通知该服务并更新作者姓名。
  2. 将关注点完全分开。每次客户端请求Post时,都会调用Blogging微服务获取Post,然后调用Users微服务根据ID获取作者姓名。

到目前为止,我倾向于解决方案 #1,因为当请求 Post 时,它只需要调用 1 个微服务,但是假设函数(微服务)的数量是否开始增长,我会不断添加一些小概念他们每个人只是为了限制调用次数,恐怕我最终会变成意大利面条Blogging微服务......但我也不认为微服务的数量会显着增长;)

你觉得哪种方法更好,为什么?是否有任何方法违反了微服务架构原则并且我的担忧是有道理的?

【问题讨论】:

    标签: microservices domain-driven-design separation-of-concerns


    【解决方案1】:

    这真的取决于你的程序的开发方式。

    解决方案 #1(编辑 Blogging 模型)

    您可以在此处编辑模型以提高性能。大多数时候,我不认为为了性能而改变模型本身是正确的方法。如果您认为 Blogging 上下文中的 User 的概念不正确,则不应将其放在那里。另一个问题是您必须在有界上下文(BC)之间处理eventual consistency。如果您认为在Blogging 中包含User 的概念是一个不错的主意,那么它可能是可行的方法。

    方案#2(单独查询数据)

    虽然这可能对性能不友好,但我认为这将是一开始最简单的方法。您不必处理最终的一致性(+处理 BC 之间的数据同步),也不必更改模型。但如果您真的关心性能并保持模型干净,您可能会对read model 感兴趣。

    另一种解决方案 - 读取模型

    读取模型的概念是至少有两个独立的模型:一个用于读取数据,一个用于写入/编辑数据。如果我们想为您的问题实现这个概念,我们将为Blogging BC 创建一个读取模型,其中PostsUsers 可以合并在一起(类似于解决方案#1)然后进行查询。我们可以借助事件来同步这些数据,因此当PostUser 发生变化时,它会引发一个事件并将更改的数据保存到您的读取模型中。通过这种方式,您可以合并/更改您想要的任何数据以获得更好的性能。它可能与解决方案 #1 非常相似,但主要区别在于您不编辑主模型。您创建一个新的只是为了阅读。这可能是最难的解决方案,但如果您的项目很大并且性能很重,这可能是一种方法。

    我不会说任何解决方案对于解决您的问题是最好的或最差的。它总是取决于上下文。

    【讨论】:

    • 您的回答很好,但您缺少 API 网关解决方案:客户端查询 API 网关,该网关将同时查询 api 和连接结果。
    • @bobkorinek,感谢您的全面回复。但是您能否向我解释一下读取模型选项与解决方案 #2 有何不同?我想最终也会分别查询两个微服务,然后只合并到不同的模型中?
    • @ArwynFr,也许我对我的问题不够具体。无论使用 API Gateway,问题都存在,因为它不是关于客户端如何请求数据,而是关于如何在后端收集所有需要的部分并在响应中发送。如果我将某些部分移动到一个微服务中,我不需要查询两个独立的部分来获取客户端所需的一切,例如我可以在Blogging 微服务中保留作者姓名,而不是每次请求Post 时都从Users 微服务中获取它。
    • @toffik325 微服务是一个独立的软件,它不应该依赖另一个服务来进行常规操作。否则,博客服务的可用性将取决于用户服务的可用性,这将产生单点故障或循环依赖的级联效应。要么你在博客服务中复制+同步用户数据,并且可以在一次旅行中获取所有数据,要么你不需要,另一个单位需要以某种方式合并数据。
    • 基本上,您在这两种解决方案之间挣扎:1- 在博客中复制用户数据会使微服务更复杂且更难维护,或者 2- 保持数据分离需要多次调用并降低客户端效率。 API 网关可以解决这些问题,方法是让您的服务保持完全独立,并通过缓存结果和执行连接为客户提供一次行程中的所有数据。