【问题标题】:Proper microservice domain boundaries适当的微服务域边界
【发布时间】:2020-03-27 00:15:52
【问题描述】:

现在我正在研究一个有趣的(至少从我的角度来看)商业案例,我必须在云环境中正确设计它。

假设我们有一个产品服务处理所有产品目录数据,例如产品层次结构、产品描述和产品关系。该服务有一个 REST API,并提供所有必要的 API 来处理产品,包括过滤、排序和搜索。

现在我们又增加了一项服务:产品价格服务。该服务处理所有与定价相关的逻辑和计算,包括折扣、客户特定价格。此服务还有一个 REST API,可为所请求的产品提供价格。

此时,画面似乎很简单:我们有 2 个职责明确的服务。从客户的角度来看,一切看起来都很简单:

  1. 请求产品
  2. 查询产品价格

现在我们有一个业务需求,要添加一个新的过滤器“低于 20 美元”。基本上,这意味着我们需要有一个完全相反的工作流程:我们需要先调用产品价格服务。此外,我们在产品服务中提供了一系列过滤器,可以轻松地与新的定价过滤器结合使用。

业务需求让我想到了这些服务的域边界,唯一的想法是边界是错误的(即使一开始没问题)。请分享您对如何在这种情况下正确定义服务架构的经验/意见。

最好的问候, 阿图尔。

【问题讨论】:

  • 产品具有属性,如颜色、尺寸等。我假设第一个服务提供这些属性并允许通过这些属性进行搜索。问题是:价格和颜色有什么区别?它们都是产品的属性,对吧?
  • @MaximSagaydachny 我同意。但是,产品属性是静态的,不会随着时间而改变,例如塑料玩具总是由塑料制成。产品价格每天都在变化,具体取决于销售活动、客户产品等各种因素。此外,价格服务包含某些繁重的计算,如税收。基本上,这就是我们有 2 项服务的原因。
  • 产品服务要价服务如何转换当前登录的客户申请什么转换?价格服务返回类似 "(current_price*0.95)" (与客户相关的产品折扣和当前全球折扣)。然后产品服务在不知道业务原因的情况下,在 SQL 中盲目使用这部分。因此,您可以显示价格并使用过滤器,而无需任何额外的服务内调用和丑陋的解决方案,即在产品从 DB 投入使用后将其从记录集中丢弃。这有点难看,但可以保持边界合乎逻辑。
  • 很遗憾,我们没有针对客户的单一折扣率。作为客户,您可以享受 20% 的工具折扣和 10% 的这些工具配件折扣 - 每个价格都非常个性化。
  • 这就是我的意思-价格服务和用户服务一起知道为某些特定类别和当前用户申请什么折扣。他们可以将折扣详细信息传递回产品服务。工作流程是:customer->query product service(filter:"price>234")->product-service-> priceSvc.getDiscount(user, productCategory); priceSvc 将折扣返回给产品 svc(这都是当前客户的上下文);产品 svc 使用折扣信息(特定于上下文)来查询数据库并向客户显示数据。产品服务不知道任何业务逻辑,但能够快速查询数据库。

标签: architecture cloud domain-driven-design microservices


【解决方案1】:

我认为你错过了关于 DDD 的一个非常重要的观点。简化 DDD 定义,它描述了当用户发送命令时如何更改您的数据(应用程序状态)。当用户想问什么时,它没有给出任何指导方针。这当然是非常高级的观点。

要回答用户提出的问题,您可以使用任何必要的方式在合理的时间内提供准确的答案。

因此,只要您不修改系统的状态,您就可以违反任何边界、读取任何私有状态或直接访问数据库来计算答案。

例如,某些架构模式(例如 CQRS)完全基于此假设。

DDD 中的边界(再次从高级角度来看)是关于如何设计编写软件部分的指南。

【讨论】:

    【解决方案2】:

    找到正确的域边界主要是为了找到设计聚合所需的最小事务边界。事务处理写入端(想想 CQRS)。对于读取端,您可以以任何有用的方式混合和匹配。这可以作为一个单独的组件实现:产品搜索服务。例如,它可能会使用 ElasticSearch。

    如果您有在拥有许多部门和文档流的大型成熟组织工作的背景,那么您可以将组织结构视为良好领域边界的示例,并从中汲取灵感。如果外部实体从该组织获得任何输出,则准备输出可能是某个专门部门的责任。

    具体谈谈您的问题,似乎客户级别或个人条件应由某些 CRM 管理。这与价格服务是分开的。而且,给定产品和基本价格,CRM 应决定应用哪个折扣或价格列。

    【讨论】:

      【解决方案3】:

      一种可能的(我认为,相当主流) 方法:API Gateway 实现API Composition pattern。在您的场景中,这意味着 API Gateway 执行以下步骤:

      1. 询问产品价格微服务,用于过滤和分页的(例如前 20 个)“低于 20 美元”的产品列表。
      2. 向产品微服务询问这 20 种产品的详细信息。
      3. 将详细的产品列表返回给调用方 Web 前端。

      这能解决你的问题吗?

      【讨论】:

      • 另一个挑战:如果我想通过来自产品服务的评级来订购这些产品怎么办?
      • 不错:)假设性能不会受到太多产品的影响: 1) 向产品价格微服务询问过滤后的产品列表“低于 20 美元”。 2)向产品微服务询问其中前20个产品的详细信息-按等级排序。 3) 同上 :) 我承认,这种方法可能需要性能优化。
      • 但是如果有数千种产品的价格低于 20 美元怎么办?我必须从定价服务中获取大量 ID,并将它们传递给产品服务进行排名。
      【解决方案4】:

      这是一个非常简单的权衡,要么您希望产品服务了解客户价格,要么您让过滤器忽略折扣,只按标价执行。这是大多数商店的过滤方式,它们不考虑个性化定价。

      如果您必须使用个性化定价,那么您需要进行经典的时间/空间权衡。您可以将所有产品预先解析为价格,使用更多空间但更少时间,或者您可以根据折扣动态计算价格,并花费更多时间。

      您可以通过将计算推到更靠近数据存储的位置来最小化该时间,但代价是架构纯度。打破架构纯度以实现性能目标是很常见的,这只是您需要明确做出的决定。

      【讨论】:

        猜你喜欢
        • 2020-09-12
        • 2021-05-17
        • 2018-08-01
        • 2020-06-27
        • 2021-04-13
        • 1970-01-01
        • 2019-04-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多