所有文章 > API设计 > 如何编写v3 AsyncAPI描述
如何编写v3 AsyncAPI描述

如何编写v3 AsyncAPI描述

当我们谈论 API 时,我们通常谈论的是我们在 Internet 上找到的那些可能实现 REST 之类的东西并通过 HTTP 提供服务的 API。随着 API 经济的发展并变得更加多样化,API 的狭隘定义已经扩大到包括其他架构风格和传输机制。在这种背景下,AsyncAPI 规范已经成为大新闻一段时间了,我们之前在博客上介绍过异步 API 的 API 描述语言。

AsyncAPI 3.0 版本于 2023 年 12 月发布,并在工具生态系统中获得了关注。我们在上一篇文章中介绍了 3.0 版本的变化,其中我们讨论了 3.0 版本支持的面向操作的视图在将操作对象与通道解耦方面的价值。操作对象的解耦开启了 AsyncAPI 用例视图的可能性,其中操作为 API 消费者描述和实现用例提供了基础。

在这篇文章中,我们将通过一个用例示例讨论 AsyncAPI v3.0 提供的主要更改,并描述这如何改进您现有的 API 设计方法。我们将使用此用例创建示例 AsyncAPI 描述,以帮助阐明该方法的优点。

 关注用例

首先,为什么要关注用例?您可能会发现专注于消息传递系统用例的想法很奇怪或有些抽象。大多数现有消息系统实现支持调用业务或技术功能的消息。但是,AsyncAPI 提供与OpenAPI相同的方法来支持设计优先的方法来创建 API。社区中的许多评论者表示,用例视图正是他们想要从 API 描述语言中得到的。 API 使用者及其利益相关者很少关注 URI 或消息队列,而是专注于 API实际执行的操作,并告诉 API 使用者必须做什么才能与给定服务集成。

操作对象以其新的解耦形式支持这种思维。操作对象不仅仅是为了描述用例而构建的,但它们的构造和抽象允许更容易地描述用例。我们可以使用基于用例创建 API 描述的想法来描述如何使用 3.0 版本编写 AsyncAPI 描述。

我们假设的示例基于使用快速医疗保健互操作性资源 (FHIR) 消息订购药物。 FHIR是由国际标准机构 HL7 创建的标准,用于发送和接收电子健康数据以及调用与医疗保健相关的操作。医疗保健领域的用例比比皆是,使用基于标准的消息定义为创建 AsyncAPI 描述提供了一个很好的起点,特别是在美国这样的市场,那里的医疗服务提供者和服务高度分散。标准化确实促进了互操作性,因为它有助于将不同的服务整合在一起,以便更有效地为患者服务。您可以在GitHub上找到我们完整的 AsyncAPI 示例。

我们订购药物的用例包含两个操作:

  1. 检索患者详细信息和用药记录:此操作允许医生检查患者之前的健康状况和当前的用药情况。
  2. 为患者订购新药物:保健医生可以为患者订购所需的药物。

此用例中还有其他步骤,但这些步骤为设计 API 提供了足够的功能,以使用 AsyncAPI v3.0 满足用例。在这篇文章中,我们将提供 AsyncAPI 描述的片段,但您可以参考完整的示例并使用AsyncAPI Studio进行更详细的研究。

创建标准化消息

创建药物订购服务的第一步是添加基于 FHIR 标准的消息结构。

采用 FHIR 并使用异步消息传递平台的系统之间的消息传递遵循使用“捆绑包”的概念,其中多个请求或消息一起发送。因此,我们创建了一种对患者数据和药物“声明”的请求的表示形式,并在一个有效负载中发送。支持这一点的模式对象是使用resourceType属性设计的,其中一个oneOf对象和resourceType值可以是PatientMedicationStatement ,例如:

- properties:
resourceType:
type: "string"
enum: ["Patient"]

架构对象遵循与 AsyncAPI 版本 2.x 和 OpenAPI 基本相同的语义。在我们的示例中,这些在组件对象中实现,以便跨消息对象重用。我们总共创建了四个,具有两个操作的请求和响应有效负载,然后实现了引用每个操作的消息对象,并且可以携带消息的语义,包括公共标头等消息特征。我们实现了一个 header health-system-id ,它提供了发送消息的系统的高级指示器。

components:
messages:
patientMedicationStatementRequest:
summary: Request message for patient and current medication data
title: Patient and Medication Request
payload:
$ref: "#/components/schemas/patientMedicationStatementRequestPayload"
traits:
- $ref: "#/components/messageTraits/healthSystemHeaders"
patientMedicationStatementResponse:
summary: Response message for patient and current medication data
title: Patient and Medication Response
payload:
$ref: "#/components/schemas/patientMedicationStatementRequestPayload"
traits:
- $ref: "#/components/messageTraits/healthSystemHeaders"
patientMedicationRequest:
summary: Request message for patient and current medication data
title: Patient and Medication Request
payload:
$ref: "#/components/schemas/patientMedicationStatementRequestPayload"
traits:
- $ref: "#/components/messageTraits/healthSystemHeaders"
patientMedicationResponse:
summary: Request message for patient and current medication data
title: Patient and Medication Request
payload:
$ref: "#/components/schemas/patientMedicationStatementRequestPayload"
traits:
- $ref: "#/components/messageTraits/healthSystemHeaders"
schemas:
patientMedicationStatementRequestPayload:
type: "object"

# Remainder of Schema Object definitions

messageTraits:
healthSystemHeaders:
headers:
type: object
properties:
health-system-id:
description: Health system provider ID as UUID
type: string
pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$

因此,AsyncAPI 的现有用户应该非常熟悉此处描述的结构。请注意,为了这篇文章的目的,我们已经大大减少了示例中的消息结构。源FHIR JSON Schema 文档要全面得多。

添加沟通渠道

消息就位后,我们创建了Channel 对象。通道描述了发送和接收消息的传输机制,并将给定的消息队列或主题与其支持的消息结构对齐。 3.0 版中对 Channel 对象的更改值得注意,因为publishsubscribe属性已被删除,并且它们引用的操作对象已移至其自己的根级属性。

操作对象的删除导致通道对象更加精简并专注于技术实现。在我们的示例中,您可以看到 Channel 定义了一个address ,它是我们消息系统中假设的消息队列,并通过messages属性提供支持的消息和通道之间的绑定:

channels:
patientMedicationStatements:
description: Channel for requesting patient record and medication statement
address: health.patient.medication.statement
messages:
patientMedicationStatementRequest:
$ref: "#/components/messages/patientMedicationStatementRequest"
patientMedicationStatementResponse:
$ref: "#/components/messages/patientMedicationStatementResponse"
patientMedicationRequest:
description: Channel for ordering medication for a patient
address: health.patient.medication.request
messages:
patientMedicationRequest:
$ref: "#/components/messages/patientMedicationRequest"
patientMedicationResponse:
$ref: "#/components/messages/patientMedicationResponse"

在 3.0 版本中,通道对象更加关注技术实现。通道的重点允许 API 设计者将重点放在操作对象上,这种抽象提高了可重用性。

描述操作

我们设计的最后一步是创建操作对象,它使我们能够告诉 AsyncAPI 描述的使用者“做什么”以将他们的应用程序与我们的 API 集成。由于操作对象的名称是给定对象的标识符,因此我们决定使用标准前缀order-medication/来命名它们,以提供对操作进行分组的方法。我们可以使用标签对象作为此方法的替代方案。

我们创建了两个操作对象, order-medication/get-patient-and-medication-statement 和 order-medication/make-medication-request 映射到我们的药物订购用例中的两个操作:

operations:
order-medication/get-patient-and-medication-statement:
summary: Retrieve the patient record and current medication for the patient
action: send
channel:
$ref: "#/channels/patientMedicationStatements"
messages:
- $ref: "#/channels/patientMedicationStatements/messages/patientMedicationStatementRequest"
reply:
address:
"location": "$message.header#/replyTo"
channel:
$ref: "#/channels/patientMedicationRequest"
messages:
- $ref: "#/channels/patientMedicationRequest/messages/patientMedicationResponse"
order-medication/make-medication-request:
summary: Send a medication request on-behalf of the patient
action: send
channel:
$ref: "#/channels/patientMedicationRequest"
messages:
- $ref: "#/channels/patientMedicationRequest/messages/patientMedicationRequest"
reply:
address:
"location": "$message.header#/replyTo"
channel:
$ref: "#/channels/patientMedicationRequest"
messages:
- $ref: "#/channels/patientMedicationRequest/messages/patientMedicationResponse"

操作引用我们已经创建的通道对象及其支持的消息。我们还在操作中利用了 3.0 版本的另一个新功能,操作回复对象。操作回复对象支持请求/回复模式的实现,这是一种非常常见的消息传递范例。在我们的示例中,我们指定了一个动态设置的回复队列,它是使用运行时表达式指定的。回复的系统在发送响应时必须使用消息头replyTo的值。

有效的 AsyncAPI 描述

将 3.0 版 AsyncAPI 描述集中在一起,重点关注特定用例,显示了 AsyncAPI 规范修订后的结构的一些重要内容,即: 添加操作对象提供了消息、传输和操作描述之间的关注点分离

除了能够描述用例之外,修订后的结构还有其他几个显着的好处:

  • 一致性:操作对象可以在整个 API 生命周期在 AsyncAPI 文档中保持静态,从而提供一致的签名。因此,操作对象更容易管理,因为它们不会“陷入”传输语义中。
  • 可移植性:将操作对象与通道对象分离意味着通道对象可以描述一次然后重复使用,包括跨 AsyncAPI 描述。这使得通道对象更加可移植。
  • 所有权:不同团队拥有其负责的 AsyncAPI 描述部分的所有权的方式补充了一致性和可移植性。例如,技术运营团队可以向 API 设计团队单独定义通道对象,并在信息可用时注入通道定义,例如服务器对象。这使得组织能够采用管道方法来创建准确的 AsyncAPI 描述,并利用自动化来确保描述尽可能准确。

我们将设计重点放在“自下而上”的方法上,使用源自开放标准的现有消息有效负载结构,并创建我们的操作对象作为最后一步。当然,您可以按照自己的方法来构建 AsyncAPI 描述(这完全取决于您),但是用例设计方法为您希望界面实际执行的操作提供了一些切实的结果。如果您愿意,您可以首先从操作对象开始,勾勒出支持的操作的要求并与设计利益相关者达成一致,然后再开始详细的消息传递设计。

在 AsyncAPI 中描述用例的主要缺点是传达顺序和依赖关系。除了通过描述和命名约定之外,AsyncAPI 不通过操作对象传达此信息。然而,提供全功能用例描述的下一步将通过 OpenAPI Initiative Arazzo规范来提供。支持 AsyncAPI 最初是在 1.0.0 版本中发布的,现在又出现在下一版本的路线图中。

Arazzo 将提供更丰富的方法来描述端到端用例,包括依赖关系、操作之间传递的动态值以及基于消息数据的故障条件。这与解耦的操作对象一起,使得通过异步 API的丰富用例描述来改善开发人员体验变得非常可行。

原文链接:https://nordicapis.com/how-to-write-a-v3-asyncapi-description/

#你可能也喜欢这些API文章!