微服务 - 在拥有多家业务的组织中

发布于 2025-01-21 12:07:19 字数 2089 浏览 1 评论 0原文

Context

Let's say we have an organization that has multiple businesses.在此示例中,企业A向大学生出售千兆互联网服务。 Business B向老年人出售Megabit Internet服务。企业出售相关产品的略有差异,每种产品都针对不同的人群。

乍一看,这似乎我们只能让一个应用程序处理所有请求。但是,鉴于每个人都针对特定的人口统计,企业自然会彼此分歧 - 从本质上讲,每个企业都会有自己的业务需求。例如,企业A可能会曝光移动应用程序供客户管理其帐户。 Business B可能会揭露必须要求客户管理其帐户的电话号码。列表继续。

在此上下文中,使用微服务的最佳方法是什么?

问题在于,不同业务之间既有常见和罕见的功能。

我们可以保持一定的干燥,并具有一组基本的微服务(计费-API,订单-API等),可以被不同的企业消费。这起作用了,但这会导致微服务具有更多的“一般”抽象 - 导致更复杂的性能。 For a concrete example, let's say the billing-api service has a /charge endpoint that is shared by Business A and B. Business B's requirement is to always discount $5 off the order:

//billing-api

if (businessB) { 
  orderCost -= 5; 
}

In this DRY approach, we would have an API gateway for每个业务(BFF模式)将汇总不同的微服务以满足其业务需求。所有“特定于业务”的逻辑都将从基本微服务转移到各个企业的API网关。 In this discount example, instead of having an if (businessB) check in the billing-api endpoint, we can invert this control to the consumer:

//billing-api

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

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

Then the endpoint in Business B's API gateway would pass in an orderDiscountAmount of 5 when calling the billing-api endpoint:

//Business B API Gateway

billingApi({ orderDiscountAmount: 5 });

This seems fine, but all we did was take Business B's logic in the billing-api endpoint and created a generic (but forced) abstraction.这是“合理的”,说可能有一天可能会使用业务 - 但这可能永远不会发生。总体而言,对于开发人员和终点的消费者来说,这感觉就像是不自然的练习。各个方面的复杂性和认知负荷都会增加。

我们可以将干燥垃圾删除,并避免在企业之间共享微服务,以最大程度地灵活性和简单性。但是,如果添加了更多的业务(10-20),那么可能会有很大一部分重复的功能。

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

如果我们可以从上方的干式方法中使用干燥的方法,那么团队应该如何结构?我们可以拥有垂直的功能团队,但这是否意味着如果我们拥有10家业务,一个团队需要在所有业务上拥有一个功能(即结帐)?这种方法的缺点是,该功能团队将不会成为整个业务中的专家 - 这些团队只能是一项特定业务中一个功能的专家。没有关于企业的完整背景可能会使做出正确的决定变得困难。

我们可以为每个专门针对UI和API网关的业务提供一个与溪流一致的团队。然后,我们将让平台团队为溪流一致的团队创建微服务。与此的缺点是,溪流一致的团队与平台团队(又称依赖性)之间存在交接步骤。

我不确定我是否会从错误的镜头中查看所有这些 - 任何反馈都将不胜感激!

Context

Let's say we have an organization that has multiple businesses. In this example, Business A sells a gigabit internet service to college students. Business B sells a megabit internet service to seniors. The businesses sell related products with slight variations, each targeting a different demographic.

At first glance, this seems like we can just have one application handle all the requests. However, it is natural for the businesses to diverge from each other given that they each target a specific demographic - by nature, each business will have its own business requirements. For example, Business A might expose a mobile application for customers to manage their account. Business B might expose a phone number that has to be called for customers to manage their account. The list goes on.

What is the best way to utilize microservices given this context?

The problem is that there is both common and uncommon functionality across the different businesses.

We can remain somewhat DRY and have a set of base microservices (billing-api, order-api, etc.) that can be consumed by the different businesses. This works but this causes the microservices to have more "general" abstractions - leading to more complexity. For a concrete example, let's say the billing-api service has a /charge endpoint that is shared by Business A and B. Business B's requirement is to always discount $5 off the order:

//billing-api

if (businessB) { 
  orderCost -= 5; 
}

In this DRY approach, we would have an API gateway for each business (BFF pattern) which would aggregate different microservices to fulfill their business needs. All "business-specific" logic would get moved from the base microservices into the respective businesses' API gateway. In this discount example, instead of having an if (businessB) check in the billing-api endpoint, we can invert this control to the consumer:

//billing-api

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

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

Then the endpoint in Business B's API gateway would pass in an orderDiscountAmount of 5 when calling the billing-api endpoint:

//Business B API Gateway

billingApi({ orderDiscountAmount: 5 });

This seems fine, but all we did was take Business B's logic in the billing-api endpoint and created a generic (but forced) abstraction. This is "justified" by saying maybe Business A may use that one day - but that may never actually happen. Overall, this feels like an unnatural exercise for the developer and the consumer of the endpoint. Complexity and cognitive load on all sides are increased.

We can scrap DRY and avoid sharing microservices between businesses for maximum flexibility and simplicity. However, if more businesses are added (10-20) then there's probably going to be a good chunk of duplicated functionality.

How should teams be structured given this context?

If we are okay with the DRY approach from above, how should teams be structured? We can have vertically-sliced feature teams, but does that mean if we have 10 businesses, a team would need to own a feature (i.e. checkout) on all the businesses? The drawback with this approach is that the feature teams won't be experts in any business as a whole - the teams would only be an expert in one feature in a given business. Not having the full context on a business could make it difficult to make the right decisions.

We can have a stream-aligned team for each business dedicated to the UI and the API gateway. We would then have platform teams creating microservices for the stream-aligned teams to consume. The drawback with this is that there is a handoff step between the stream-aligned team and the platform team, a.k.a a dependency.

I'm not sure if I'm looking at all this from the wrong lens - any feedback would be appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

呢古 2025-01-28 12:07:19

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

话虽这么说,我并不害羞,无法提出我的意见或至少对您所描述的情况的一些想法。

  1. 我相信您关于如何建立团队以及如何建立体系结构的问题非常紧密地联系在一起,因为毫无疑问,建筑会有效地遵循组织结构。因此,我将首先对组织设置进行进一步的想法。
  2. 估计每个业务所需的人力总额,应该使您了解需要多少团队。试图使团队规模保持小(例如2-8人)将有助于减少开销的交流。因此,如果您认为这是整个业务的规模,那么就无需进一步分担责任。
  3. 责任是最重要的关键字。您必须避免使用通用服务/库但有多个或没有所有者的任何情况。总是应该有一个组织所有者。因此,当组织认识到在不同领域的功能重叠时,建立将负责并向他人提供此功能的团队是一种普遍做法。这可能是以共享库的形式或实际部署的服务。在这两种情况下通用功能。
  4. 在您的问题描述中,问题的核心是业务逻辑及其复杂性 /重叠。因此,我认为最重要的角色是产品管理。他们必须非常好(至少有些技术),并将这些确切的混乱分类为可重复使用的作品和仅特定于单个业务的事物。如果您拥有整个产品经理团队,他们需要很好地进行交流并共同构建这张照片。这里最重要的是关于未来的 Vision 的良好沟通,而不仅仅是直接要求(提供了一个很棒的域视图)。只有这样,架构和团队才能以最好的方式建立。
  5. 无论初始设置多么谨慎 - 都会发生更改。无论您认为什么是最好的解决方案,最好的解决方案都会在将来的某个时候发生变化。为了为此做准备,我总是建议采用最简单的方法 - 即使这意味着某些代码重复或其他缺陷。作为软件架构师,我们倾向于喜欢完美的美感,但这并不是现实世界中最有效的方法。
  6. 通常可以通过添加一些可配置性来制作一个简单的共享服务/库来适合多个用例。在某种程度上,这是一种有用的方法,但是对于该图书馆/服务的消费者来说,您必须很明智,并且在任何时候都应该很容易重复使用。何时到达功能变得太大 /复杂,必须分为多个部分才能维护,但是用维护者的眼睛看它,消费者的眼睛将使决心更加容易。对于可配置的服务库,您也可以使用具有不同配置的单独部署,因此使用常用的组件,但为每个用例部署不同的端点。如果您使用仅生产小部件开销的技术(例如,只有几个MB的Golang容器),那么大量部署的服务不是缺点,而是强度,因为它们可以独立升级 /版本,甚至是易于并行运行多个版本。
  7. 基础架构和服务部署可能会或可能不会遵循服务的体系结构。作为一般规则,我建议寻找最简单的方法,这通常意味着提供在服务之间共享的常见基础架构和部署配置是服务之间的区别。例如,所有服务共享一个常见的群集 /流媒体 /网关 /数据库 /等。对此的例外可能是单个服务的非常特殊的需求,例如硬件加密密钥存储或用于机器学习的GPU服务器等。这是方法对于任何合理的尺寸系统。 (当然,如果您要扩展到非常大的尺寸,这也是一种非常可行的方法,可以为特定服务提供完整的堆栈 /集群。)
  8. 持久性设计至关重要。在相对容易发展业务逻辑,重组它等相对容易的地方,很难发展您的历史数据。通常,您可以选择以两种方式之一进行设计:
    1. 智能算法,愚蠢的数据。
    2. 智能数据,愚蠢的算法。

(明智地提到更多详细 /反映更多业务需求)
最初,第二种方法通常更难,但是根据我的经验,当达到一定的复杂性阈值时,结果将获得更好的结果。

因此,在阅读您的问题时,这些只是我脑海中出现的几件事。抱歉,他们无法回答有关如何切割计费API的详细问题,但也许您还有一些其他考虑因素。

Sorry to say, but this is not a good question for Stackoverflow, because any answer will be a opinion based and many approaches may work and depend on more details in your specific use case. So don't be disappointed if the question get's closed at some point.

That being said I am not too shy to offer my opinion or at least some thoughts about your described situation.

  1. I believe your question about how to set up teams and how to set up the architecture are very tightly linked because architecture will with no doubt follow the organizational structure effectively. So I will give further thoughts about the organizational setup first.
  2. An estimation of the total manpower required for each of the businesses should give you an idea of how many teams you need. Trying to keep team size small (say 2-8 people) will help reducing the communication overhead. So if you think this is the size for a whole business then there is no need to further split responsibility.
  3. Responsibility is the most important keyword. You have to avoid any situation where a common service/library is used but has multiple or no owners. There should always be exactly one organizational owner. Thus, when organizations recognize the overlap of functionality in separate areas it is a common practice to establish a team that will be responsible and provide this functionality to others. This could be in the form of shared libraries or actually deployed services. In both cases it is important that the communication is formalized by correctly versioning their work and leaving it to the consuming groups which versions to use, when to upgrade, putting in requests for new features, etc. This approach will decouple the teams that use this common functionality.
  4. In your problem description the core of the problem is the business logic and it's complexity / overlap. So I would argue that the most important role is the product management. They have to be very good (and at least a bit technical) and sort this exact mess into reusable pieces and things that are specific to only a single business. If you have a whole team of product managers they need to communicate very well and build this picture together. What is most important here is a good communication about the vision for the future and not just immediate requirements (Provide a great domain view). Only then can the architecture and teams be set up in the best possible way.
  5. No matter how careful the initial setup - Changes WILL happen. Whatever you think in the beginning to be the best solution will change at some point in the future. In order to prepare for this I always recommend to go with the simplest approaches - Even if it means some code duplication or other imperfections. As software architects we tend to love the beauty of perfection, but that is rarely the most effective approach in the real world.
  6. It is common sense to make a simple shared service/library that can be made to fit multiple use cases by adding some configurability. Up to certain degree of complexity that is a useful approach, but you have to be sensible to the consumers of that library/service and it should be easy to reuse at any point. It is not black and white about when to a functionality becomes too big / complex and has to be split into multiple pieces to be maintainable, but looking at it with the eyes of the maintainer and the eyes of consumer will make a determination easier. In the case of configurable service libraries you could also have separate deployments with different configurations, so using commonly developed components, but deploying different endpoints for each use case. If you use technologies that produce only a small deployment overhead (for example golang containers that are only a few mbs), then the large number of deployed services is not a drawback but a strength because they can be upgraded / versioned independently and it is even easy to run multiple versions in parallel.
  7. Infrastructure and service deployment may or may not follow the architecture of the services. As a general rule I would recommend to look for the simplest approach, which often means providing common infrastructure that is shared among services and deployment configurations are where the distinction between services starts. For example a all services share a common cluster / streaming / gateways / databases / etc. Exceptions to that could be very special needs for single services, like a hardware encryption key store or GPU servers for machine learning, etc. This would be the approach for any reasonable sized system. (Of course if you are going scale to very large sizes it also a very feasible approach to have complete stacks / clusters for specific services.)
  8. Persistency design is most crucial. Where it is relatively easy to evolve a business logic, reorganize it, etc., it is rather difficult to evolve your historic data. Often you have a choice to do a design in one of two ways:
    1. Smart algorithms, dumb data.
    2. Smart data, dumb algorithms.

(Smart referring to more elaborate / reflecting more of the business requirements)
The second approach is usually harder initially, but in my experience will have better results when a certain complexity threshold is reached.

So these are just a few things that came to my head when reading your question. I apologize that they cannot answer your detailed question about how to slice the billing API, but maybe you have a few additional considerations at hand.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文