使用RESTful服务和应用程序API实现SOA?

6

目前我们只有一个大型API,用于后台管理、前端和公共API。

这给我带来了很多问题,因为当我构建新的端点时,代码中会包含很多应用特定的逻辑,而我不一定想在我的端点中包含它。例如,创建用户的代码可能包含发送欢迎电子邮件的代码,但由于后台管理端点不需要该逻辑,我将需要添加一个新的端点而没有那个逻辑。

我考虑进行一次大规模的重构,将我们的代码库拆分成多个小型、高度专业化的服务API,然后在这些API之上构建一组小型的应用程序API。

因此,例如,在重构后,创建新用户的应用程序端点可能会执行以下操作:

customerService.createCustomer();
paymentService.chargeCard();
emailService.sendWelcomeEmail();

应用程序和服务API将是完全分离的代码库(也许每个服务都有一个单独的代码库),它们也可能使用不同的语言进行构建。它们只通过REST API调用进行交互。它们将在相同的本地网络上,因此延迟不应该是一个巨大的问题。
这是一个不好的想法吗?我从未见过/使用过将两者分开的代码库,因此也许有更好的架构来实现我所寻求的灵活性和可维护性?
建议、链接或评论都将不胜感激。

创建几个专用且可重复使用的服务确实是一个好主意。当我看到你的工作流程时,我认为你应该尝试使用事件驱动架构。因此,当创建用户时,您的应用程序只需发送一个带有新用户联系信息的事件。另一个专用应用程序将等待此类事件并向新客户发送邮件。如果您有多个此类交互,则引入事件总线可能值得一试。如果不是这样,那么您的方法已经是有效的。 - Arnaud Develay
3个回答

2
您制定多个明确定义的服务的想法是明智的,这确实是处理此问题的最佳方法。然而,纯微服务方法无论看起来多么时髦,往往都会证明过度杀伤。这就是为什么我会重新设计现有的API/服务,并遵循坚实和可靠的SOA设计原则。可以在serviceorientation.comsoapatterns.org上找到好的资源。在我的职业生涯中,我一直将它们用作参考。

考虑您需要哪些类型的服务

服务类型和层(图片来源于serviceorientation.com)

实体服务通常是您的客户端、支付服务等,即以您领域中的实体为中心的服务。它们应该与业务无关,并且能够在所有情况下重复使用。如果满足其需求,有时客户可以直接调用它们。任务服务可能会调用它们。
实用程序服务包含您可能会在其他服务中重复使用的逻辑,但通常不会由客户直接调用。相反,它们将由任务和实体服务调用。例如,转换服务可能是一个例子。
任务服务将实体和实用程序服务组合并重复使用,形成有意义的任务。它们通常不那么通用,并且实现一些特定的业务逻辑。它们具有有意义的业务操作,并且是客户主要调用的服务。
重新设计时应遵循的原则:
我强烈建议您查看这份备忘单,确保您的重新设计覆盖了其中的所有内容。这是很有帮助的。
总的来说,您应该确保:
每个服务都有一个共同的上下文,并遵循关注点分离原则。例如,客户服务仅用于与客户相关的操作等。
实体和实用程序服务都是业务无关且基本的。因此,它们可以在多种情境和上下文中重复使用而不需要更改。合同必须简单-只有CRUD和在大多数使用场景中有意义的常见操作。
服务遵循通用数据模型-确保您使用的所有数据结构在所有服务中都统一使用,以防止未来需要进行集成工作,并促进客户端利用服务的组合。如果您需要接收另一个服务返回的客户,则应该无需转换即可完成。
好的,但是哪里放置非通用逻辑?
现在,每当您需要复杂的业务功能时,您有多个抽象业务逻辑的选项。选择哪个取决于您的情况:
  • 让所有客户端都遵循逻辑。让他们组合您的简化服务。
  • 如果有业务逻辑在多个应用程序中通常实现并且具有重复使用的潜力,您可以实现一个组合服务,该服务重复使用多个现有的底层服务并公开逻辑。

服务可组合性。关注多个API调用通信开销。

好吧,这是一个古老的问题 - 当它们可能会创建一些通信开销时,您是否应进行多个API调用?答案是 - 这取决于您的场景有多么复杂,您期望多少重用以及您希望有多大的灵活性。速度是否至关重要?在多大程度上?在面向服务的架构中,这是一种非常常见的方法 - 重用现有服务并根据需要将它们组合在新配置中。是的,它确实增加了一些开销,但我已经看到过在非常复杂的环境中(例如电信)的实现,由于使用ESB解决方案,消息队列等,开销与收益相比微不足道。这里是一种常见的架构方法(来自serviceorientation.com的图像):

Composing Web Services to Solve a Bigger Problem

必要的遗留代码重构提醒

往往情况下,改变多个现有客户系统的既定合同是一项混乱的任务,可能需要进行大量的重构并需要查找深藏在(可能)遗留代码中的功能。业务逻辑可能分散在各个地方。因此,请确保您已经准备好并具备控制、时间和意愿来领导这场战斗。

希望这可以帮到您。


1

这是个不好的想法吗?

不是,但这是一个需要提供非常具体建议的大问题。

我想将其分为三个方面:

  • 方法
  • 设计
  • 技术

往回推,技术是最终和最具体的部分,完全取决于您当前的环境(平台,技能),一旦其他事情开始进展,它将是合理自明的。

您上面概述的设计似乎是一个很好的最终状态 - 拥有多个特定且专注的API,每个API都有自己的职责。同样,设计的细节将取决于您和组织的技能以及现有的平台。例如,如果您已经在使用TIBCO(例如)并且投入了大量资源(许可证,平台,工具,人员),那么利用他们发布的一些模式/设计/模板是有意义的;但如果您还没有接触过TIBCO,则可能不是最佳选择。

在抽象层面上,REST API服务似乎是一个很好的起点 - 系统的各个层面都有许多安全、部署、监控、可扩展性等方面的工具和平台。如果您是NGINX用户,他们也有很多(独立于平台的)想法NGINX博客,包括一些关于可扩展性和性能的聪明思考。如果您更加冒险,并且有一个聪明、渴望的团队,可以看看事件驱动架构 - 参见this 方法(或过程)是关键。最终,这是一次重构,尽管你所描述的“大规模重构”有点让我感到害怕——这样说,听起来像是你在谈论一个大规模的变化并将其称为重构。也许这只是语言问题,但在我的想法中,应该是“将‘一个巨大的API’逐步演变为多个具体、专注的API(通过重构架构)”。其中一个起点是Martin Fowler,虽然这本书是关于软件重构的,但原则和方法是相同的,只是在更高的层面上。实际上,他就在这里谈到了这个问题。
IBM谈论重构为微服务,并使其听起来很容易一步完成,但在现实中却从未如此(除了实验室)。

您现有一个API,为多个内部和外部客户提供服务。我建议您保持这个接口对于这些客户的稳定性 - 将重构实现与联络和协调外部系统/组的其他问题分开。我的高级起始方法将是:

  • 确定API中与之相关的少量(3-7个)方法
    • 如果这些方法需要进行重大且有限范围的更改,那就很好——代码变更带来了商业价值
  • 为这些方法设计/指定一个新的独立API
    • 首先,克隆现有模型/命名/样式
  • 仅为这些方法编写新服务
    • 采用适当的自动化CI/CD测试和部署实践
    • 配备相关监控
  • 修改现有API,使调用这些方法的调用重定向到调用新服务
    • 也许可以设置运行时开关,在旧实现和新实现之间切换
  • 从代码库中删除旧实现
  • 沿途捕获问题、假设和问题
    • 第一次尝试将涉及到学习什么有效,什么无效。
  • 然后不断重复这个过程,每次都纳入改进。
在未来的某个时候,如果出于其他业务需求适当的情况下,向后端、前端和/或公共客户端发布的API可能会发生变化,但这是一个完全不同的项目。
正如您所看到的,如果API很大(1,000个方法=> 140个版本),这是一个数月的过程,并且拥有一个相当频繁的发布计划非常重要。并且,改进可靠且永远不会更改的代码可能没有价值,因此现有API的(潜在)大部分可能仍然存在,只是由新的API包装。
其他考虑事项:
  • 公共API?也许需要一个新版本(显著更改)比内部API更快地实现。
    • 重点关注使用该方法/服务的内容。
  • 哪些部分/服务变化最大(有最多增强请求已批准)?
    • 这些位最可能发生变化,并且可以从更好的流程/架构中受益。
  • 未来的变化计划是什么,API将受到影响的位置在哪里?
    • 例如用户管理的更改、支付处理器的更改、履行系统的更改等。
    • 例如新的业务计划(新产品/服务)。
    • 请考虑API中受影响的方法。
  • 另请参见:

我能给出的最大的4个建议是:

  • 考虑重构:进行小型的更改,不影响功能
  • 考虑敏捷:进行小型的增量,有价值、可测试、可实现
  • 持续思考:设想你(最终)要达到的目标,然后不断地进行流程工作
    • 从编码、文档、测试、部署、监控等方面进行脚本化和自动化处理
    • 每次都进行改进!
  • 你有一个运行良好的应用程序/API - 保持它的运行!
    • 这始终是第一优先级(你只需要努力争取时间/预算进行维护)

嘿,非常感谢您提供的所有信息。我稍后会查看您提供的链接。作为一个快速跟进,您是否对我的双层API方法有任何具体想法?例如,我们在前端运行Angular,并且从前端进行三个不同的服务API调用仅仅是为了注册客户(见我的示例),这似乎是不好的设计,这就是为什么我考虑添加第二层的原因。也许有更典型的解决这个问题的方式吗? - user7862512
你最终会得到不同的层,具有不同的职责和粒度。在较低的级别上,您将拥有具有相当专注和特定职责的服务/API,这意味着它们的API/接口非常细粒度。更容易避免内聚并维护这些服务/API/接口,但对于您的客户来说更加复杂。因此,对于更加客户/用户/业务关注的API,这类似于一个编排层 - 请参见https://thenextweb.com/dd/2013/12/17/future-api-design-orchestration-layer/以获取快速示例。 - Brian Quinn

1
不错的想法。
另外,您所关注的是微服务架构。在此背景下,问题是如何将系统分解为定义良好的服务。
我们使用领域驱动设计架构将系统分解为微服务和 lagom框架,该框架允许每个服务在不同的代码库中,并且在微服务之间采用事件驱动架构。
现在让我们从低级别来看您的问题:您说一个服务包含创建用户和发送电子邮件等代码,还有一个只包含创建用户的代码,可能还有其他代码。
首先,我们需要了解您正在编写多少种类型的代码:
1. 领域对象逻辑(例如:用户对象)--哪些参数是有效的等--这应该独立于服务端点,并应封装在一个类中,例如用户类,我们称其为领域驱动设计术语中的聚合。
2. 业务反应--例如在用户创建时发送电子邮件--使用事件驱动架构,这些类型的逻辑被分离到流程管理器或saga中,大多数情况下可以有条件地工作,例如对于外部创建的用户,发送邮件,对于内部创建的用户,发送电子邮件,通过在事件中添加额外数据。

同时,您目前的做法是如何处理跨服务的事务的?


我们目前有一个单体Java后端。我考虑将其拆分为较小的代码库,并使用REST在服务之间进行事务处理。我真的不知道如何设置微服务。我相信这可能不是最好的方法,但到目前为止,我还没有找到“正确”的方法来做我想做的事情。 - user7862512
跨服务的事务处理REST --- 这是什么意思,你可能需要一个saga或进程管理器。此外,您可以使用领域驱动设计首先了解系统应该如何分解,然后对于实现,您可以使用Lagom。 - techagrammer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接