微服务——在拥有多个业务的组织中

Microservices - in an organization with multiple businesses

上下文

假设我们有一个拥有多项业务的组织。在此示例中,企业 A 向大学生销售千兆互联网服务。企业 B 向老年人销售兆位互联网服务。这些企业销售的相关产品略有不同,每个产品都针对不同的人群。

乍一看,这似乎我们可以让一个应用程序处理所有请求。但是,鉴于每个企业都针对特定的人群,因此企业之间自然会出现分歧——从本质上讲,每个企业都会有自己的业务需求。例如,企业 A 可能会公开一个移动应用程序供客户管理他们的帐户。企业 B 可能会公开一个 phone 号码,客户必须拨打该号码才能管理他们的帐户。清单还在继续。

在这种情况下,利用微服务的最佳方式是什么?

问题是不同的企业既有共同的功能也有不常见的功能。

我们可以保持一定程度的 DRY,并拥有一组可供不同业务使用的基本微服务(计费-api、订单-api 等)。这行得通,但这会导致微服务具有更多的“通用”抽象——从而导致更多的复杂性。举一个具体的例子,假设 billing-api 服务有一个由企业 A 和 B 共享的 /charge 端点。企业 B 的要求是总是在订单上打折 5 美元:

//billing-api

if (businessB) { 
  orderCost -= 5; 
}

在这种 DRY 方法中,我们将为每个业务(BFF 模式)提供一个 API 网关,它将聚合不同的微服务以满足他们的业务需求。所有“特定于业务”的逻辑都将从基础微服务转移到各自业务的 API 网关中。在这个折扣示例中,我们可以将此控件反转给消费者,而不是在 billing-api 端点中进行 if (businessB) 检查:

//billing-api

const { orderDiscountAmount } = req.body; //body parameters

if (orderDiscountAmount > 0) { 
  orderCost -= orderDiscountAmount; 
}

那么在调用 billing-api 端点时,企业 B 的 API 网关中的端点将传入 orderDiscountAmount 5:

//Business B API Gateway

billingApi({ orderDiscountAmount: 5 });

这看起来不错,但我们所做的只是在 billing-api 端点采用业务 B 的逻辑,并创建了一个通用的(但强制的)抽象。这是“有道理的”,因为企业 A 可能有一天会使用它——但这可能永远不会真正发生。总的来说,对于端点的开发人员和消费者来说,这感觉像是一种不自然的练习。各方面的复杂性和认知负荷都增加了。

我们可以废弃 DRY 并避免在企业之间共享微服务,以实现最大的灵活性和简单性。但是,如果添加更多的业务 (10-20),那么可能会有大量重复的功能。

在这种情况下,团队应该如何构建?

如果我们同意上面的 DRY 方法,那么团队应该如何构建?我们可以有垂直切片的功能团队,但这是否意味着如果我们有 10 个业务,一个团队需要拥有所有业务的功能(即结帐)?这种方法的缺点是特性团队不会成为任何业务的专家——这些团队只能是给定业务中一个特性的专家。不了解业务的完整背景可能会难以做出正确的决策。

我们可以为每个业务配备一个流对齐的团队,专门负责 UI 和 API 网关。然后,我们会让平台团队创建微服务,供流对齐团队使用。这样做的缺点是流对齐团队和平台团队之间有一个切换步骤,a.k.a 依赖。

我不确定我是否从错误的角度看待这一切 - 任何反馈将不胜感激!

抱歉这么说,但这对 Whosebug 来说不是一个好问题,因为任何答案都是基于意见的,许多方法都可能有效,并且取决于您的特定用例中的更多细节。因此,如果问题在某个时候被关闭,请不要失望。

话虽如此,我并不害羞地发表我的意见或至少对您描述的情况有一些想法。

  1. 我相信你关于如何建立团队和如何建立架构的问题是紧密相连的,因为架构无疑会有效地遵循组织结构。因此,我将首先对组织设置进行进一步的思考。
  2. 每个企业所需的总人力估算应该可以让您了解您需要多少团队。尝试保持团队规模较小(比如 2-8 人)将有助于减少沟通开销。因此,如果您认为这是整个企业的规模,那么就没有必要进一步分担责任。
  3. 责任是最重要的关键词。您必须避免任何使用公共 service/library 但有多个所有者或没有所有者的情况。应该始终只有一个组织所有者。因此,当组织认识到不同领域的功能重叠时,通常的做法是建立一个负责并将此功能提供给其他人的团队。这可以采用共享库或实际部署服务的形式。在这两种情况下,重要的是通过正确地对他们的工作进行版本化并将其留给消费组使用哪个版本、何时升级、提出新功能请求等来正式化沟通。这种方法将使使用此方法的团队解耦常用功能。
  4. 在您的问题描述中,问题的核心是业务逻辑及其复杂性/重叠。所以我认为最重要的角色是产品管理。他们必须非常优秀(并且至少有一点技术性),并将这些完全混乱的东西分类为可重复使用的部分和仅特定于单个企业的东西。如果你有一个完整的产品经理团队,他们需要很好地沟通并共同构建这个画面。这里最重要的是关于 愿景 未来的良好沟通,而不仅仅是眼前的需求(提供出色的 域视图 )。只有这样,架构和团队才能以最佳方式设置。
  5. 无论初始设置多么小心 - 都会发生变化。无论你一开始认为什么是最好的解决方案,都会在未来的某个时候改变。为了为此做好准备,我总是建议采用最简单的方法——即使这意味着一些代码重复或其他缺陷。作为软件架构师,我们往往喜欢完美之美,但这在现实世界中很少是最有效的方法。
  6. 制作一个简单的共享 service/library 是常识,它可以通过添加一些可配置性来适应多个用例。达到一定程度的复杂性是一种有用的方法,但您必须对 library/service 的消费者敏感,并且它应该在任何时候都易于重用。功能何时变得太大/复杂并且必须拆分成多个部分才能维护并不是黑白分明的,但是用维护者的眼睛和消费者的眼睛来看待它会让决定变得更容易。在可配置服务库的情况下,您还可以使用不同的配置进行单独的部署,因此使用共同开发的组件,但为每个用例部署不同的端点。如果您使用的技术只会产生很小的部署开销(例如只有几 MB 的 golang 容器),那么大量部署的服务不是缺点而是优势,因为它们可以独立升级/版本化,甚至是易于运行多个版本并行。
  7. 基础架构和服务部署可能遵循也可能不遵循服务的体系结构。作为一般规则,我建议寻找最简单的方法,这通常意味着提供在服务和部署配置之间共享的公共基础设施是服务之间开始区分的地方。例如,所有服务共享一个公共集群/流媒体/网关/数据库/等。例外情况可能是对单个服务的非常特殊的需求,例如硬件加密密钥存储或用于机器学习的 GPU 服务器等。这就是方法对于任何合理大小的系统。 (当然,如果您要扩展到非常大的规模,那么为特定服务提供完整的堆栈/集群也是一种非常可行的方法。)
  8. 持久性设计最为关键。在业务逻辑的发展、重组等相对容易的地方,发展你的历史是相当困难的c 数据。通常,您可以选择以两种方式之一进行设计:
    1. 聪明的算法,愚蠢的数据。
    2. 聪明的数据,愚蠢的算法。

(智能指的是更详细/反映更多的业务需求) 第二种方法一开始通常比较难,但根据我的经验,当达到一定的复杂度阈值时会有更好的结果。

所以这些只是我在阅读您的问题时想到的一些事情。很抱歉,他们无法回答您有关如何分割帐单的详细问题 API,但也许您手头还有一些其他注意事项。