Swagger vs OpenAPI 3.1:核心概念与规范演进

很多刚入行的同学一听到 Swagger 和 OpenAPI 就头大,觉得这俩词儿是不是同一个东西?可以这么理解,这俩确实是一家子,但属于“父子关系”或者说“进化关系”。

早些年,大家说的 Swagger 其实是 SmartBear 公司维护的一套工具集(包括 Swagger Editor, Swagger UI, Swagger Codegen)。后来,社区觉得这玩意儿太重要了,得有个统一的标准,于是 Swagger 的规范部分被捐赠给了 Linux 基金会,这就是 OpenAPI Initiative (OAI)。从那以后,“规范”这部分就改名叫 OpenAPI Specification (OAS),而 Swagger 则回归到了工具实现的层面。

咱们现在写代码,最该关注的是 OpenAPI Specification 3.1.0。这个版本是 2021 年 2 月发布的,别看它是 2021 年的,到现在依然是最新的稳定版。它最大的杀手锏是什么?就是完全兼容 JSON Schema 2020-12

这意味着啥?意味着你在写 API 定义的时候,JSON Schema 里那些高级特性,比如 if/then/else 条件判断、更灵活的类型定义,在 OpenAPI 3.1 里都能用。以前 3.0 版本的时候,OpenAPI 对 JSON Schema 的支持是“阉割版”的,很多特性用不了,升级到 3.1 之后,这种割裂感就没了,定义复杂数据结构的时候爽得飞起。

规范演进带来的变化

在 3.1 版本里,有一个细节特别值得注意:type 字段支持数组了

以前在 JSON Schema 里,你可以写 type: ["string", "null"] 来表示这个字段可以是字符串或者空值。但在 OpenAPI 3.0 里,这会被解析器报错。到了 3.1,这就完全合法了。这对于那些习惯用 null 表示空值的开发者来说,简直是福音。

另外,咱们社区里现在讨论最火的话题之一就是“要不要升级到 3.1”。我的建议是,新项目直接上 3.1,别犹豫。老项目如果接口多,可以慢慢迁,因为 3.1 对 3.0 的兼容性还是很好的,主要是底层 Schema 解析引擎变了。

🔧 实战技巧

注意:如果你在项目中看到有人还在用 Swagger 2.0 的注解(比如 @Api 这种),别犹豫,赶紧让他重构。Swagger 2.0 已经是“老古董”了,虽然还能跑,但很多新特性(比如上面说的 JSON Schema 兼容)都享受不到,而且现在的 SpringDoc 等主流工具链对 OAS 3.x 的支持才是最丝滑的。

下面是一个标准的 OpenAPI 3.1 的 YAML 片段,你可以感受一下它的结构,注意看 type 那里的写法:

openapi: 3.1.0 info: title: 用户管理API version: 1.0.0 description: 这是一个演示 OpenAPI 3.1 特性的示例 paths: /users/{id}: get: summary: 获取用户信息 parameters: - name: id in: path required: true schema: type: integer responses: '200': description: 成功返回用户详情 content: application/json: schema: type: object properties: username: type: string # OpenAPI 3.1 的新特性:支持多类型定义 email: type: - string - "null" description: 邮箱可能是字符串,也可能是空值 role: type: string # 支持 JSON Schema 2020-12 的枚举写法 enum: ["admin", "user", "guest"]

Spring Boot 3集成Swagger:注解驱动与代码实战

现在主流的 Java 开发基本都跑在 Spring Boot 3 上了。在 Spring Boot 3 里集成 Swagger(准确说是集成 OpenAPI 规范实现),咱们不用老一套的 springfox-swagger2 了,那个东西在 Spring Boot 3 上简直是“雷区”,各种报错。现在业界的标准答案是用 springdoc-openapi

简单来说,springdoc-openapi 就是目前连接 Spring Boot 和 OpenAPI 规范的最佳桥梁。它能自动扫描你的 Controller,把那些注解翻译成 OpenAPI 3.1 的文档。

快速集成步骤

咱们直接上干货。首先,在你的 pom.xml 里加上这个依赖。注意,Spring Boot 3 用的是 Jakarta EE 而不是 javax,所以一定要用 springdoc-openapi-starter-webmvc-ui 这个包。

<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.3.0</version> </dependency>

加完依赖,你甚至不需要写任何配置类,直接启动项目,访问 http://localhost:8080/swagger-ui.html,文档就出来了。但这样生成的文档太简陋,咱们得用注解把它“武装”起来。

注解驱动实战

在 Spring Boot 3 里,咱们常用的注解都变了。以前是 @Api@ApiOperation,现在换成 io.swagger.v3.oas.annotations 包下的注解了。

来看一个完整的 Controller 和 DTO 示例,这是我在实际项目中常用的写法:

package com.example.demo.controller; import com.example.demo.dto.UserDTO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/v1/users") @Tag(name = "用户管理", description = "用户相关的增删改查接口") public class UserController { @GetMapping("/{id}") @Operation(summary = "根据ID获取用户", description = "通过用户ID查询详细信息,如果找不到返回404") public UserDTO getUserById( @Parameter(description = "用户唯一标识", required = true, example = "123") @PathVariable Long id) { // 模拟返回数据 UserDTO user = new UserDTO(); user.setId(id); user.setUsername("coder_" + id); user.setEmail("test@example.com"); user.setRole("admin"); return user; } @PostMapping @Operation(summary = "创建新用户", description = "传入用户JSON对象,返回创建后的用户ID") public String createUser(@RequestBody UserDTO userDTO) { // 模拟创建逻辑 return "User created with id: " + userDTO.getId(); } }

再来看看 DTO 的写法,这里用 @Schema 来定义字段含义和示例:

package com.example.demo.dto; import io.swagger.v3.oas.annotations.media.Schema; @Schema(description = "用户数据传输对象") public class UserDTO { @Schema(description = "用户ID", example = "1001", accessMode = Schema.AccessMode.READ_ONLY) private Long id; @Schema(description = "用户名", example = "zhangsan", requiredMode = Schema.RequiredMode.REQUIRED) private String username; @Schema(description = "邮箱地址", example = "zhangsan@mail.com", nullable = true) private String email; @Schema(description = "角色", example = "admin", allowableValues = {"admin", "user"}) private String role; // Getter 和 Setter 省略,实际开发记得加上 public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } }

⚡ 效率提示

经验之谈提醒:很多新手在 Spring Boot 3 里加了依赖却发现访问不了 /swagger-ui.html。这时候先别慌,检查一下你的拦截器(Interceptor)或者安全框架(Spring Security)是不是把路径给拦了。在 Spring Security 里,你需要把 /swagger-ui//v3/api-docs/ 这些路径放行,否则你看到的只会是一个空白页或者 403 错误。还有,别忘了生产环境一定要通过配置或者 Profile 把 Swagger 关掉,不然这就是个巨大的安全漏洞。

升级OpenAPI 3.1:兼容JSON Schema 2020-12与性能优化

如果你的项目正在从旧版迁移,或者你想用上最新的特性,那咱们就得聊聊怎么彻底拥抱 OpenAPI 3.1。正如前文提到的,这版本最大的亮点是完全兼容 JSON Schema 2020-12

这听起来很技术,但对咱们开发者来说,最直观的好处就是写 API 定义的时候更“随心所欲”了。比如,以前定义一个字段,你想让它既是数字又是字符串,这在 3.0 里很难办,但在 3.1 里,直接利用 JSON Schema 的特性就能搞定。

兼容 JSON Schema 2020-12 的威力

咱们来看一个稍微复杂点的例子。假设你要定义一个“支付请求”,里面有个 amount 字段,有时候前端传字符串 "100.00",有时候传数字 100。在旧规范里,你可能需要写两个字段或者做兼容处理,但在 OpenAPI 3.1 里,你可以这样写:

openapi: 3.1.0 info: title: 支付服务API version: 2.0.0 paths: /pay: post: summary: 发起支付 requestBody: content: application/json: schema: type: object properties: orderId: type: string # 重点在这里:利用 JSON Schema 2020-12 的多类型支持 amount: type: [number, string] description: 金额,可以是数字也可以是字符串格式的数字 # 还可以用 if/then 做复杂校验 paymentType: type: string enum: ["card", "transfer"] cardNumber: type: string description: 如果是卡支付,这里填卡号 # 这里是 JSON Schema 的高级用法,在 3.1 中完美支持 if: properties: paymentType: const: "card" then: required: ["cardNumber"]

这种灵活性是 3.1 版本带来的巨大提升,特别是对于那些对接了多种异构系统的场景。

大型项目的性能优化

咱们社区里经常有人吐槽,说 Swagger UI 在接口多了之后加载特别慢,甚至能把浏览器卡死。这确实是个痛点,尤其是微服务里接口成千上万的时候。

核心要点:解决性能问题,不能只靠换硬件,得靠“分而治之”。

下面是一个在 Spring Boot 里配置分组的代码示例,这招非常管用:

package com.example.demo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Info; import org.springdoc.core.models.GroupedOpenApi; @Configuration @OpenAPIDefinition(info = @Info(title = "电商平台API", version = "1.0")) public class OpenApiConfig { @Bean public GroupedOpenApi publicApi() { // 只扫描路径包含 /api/v1/public 的接口 return GroupedOpenApi.builder() .group("公共接口") .pathsToMatch("/api/v1/public/**") .build(); } @Bean public GroupedOpenApi adminApi() { // 只扫描路径包含 /api/v1/admin 的接口 return GroupedOpenApi.builder() .group("管理后台接口") .pathsToMatch("/api/v1/admin/**") .addOperationCustomizer((operation, handlerMethod) -> { // 这里还可以针对特定组做定制化处理 return operation; }) .build(); } }

📖 学习建议

技术选型:关于工具链,市面上还有个热门话题是 Swagger Codegen 和 OpenAPI Generator 怎么选。可以这么理解,OpenAPI Generator 是 Swagger Codegen 的一个 fork(分支),现在社区更活跃,支持的语言和模板更新更快。如果你要生成客户端 SDK 或者服务端骨架,我建议直接用 OpenAPI Generator,别再守着老旧的 Codegen 了,不然生成的代码可能会有很多兼容性问题。而且,配合上 2024 年的趋势,很多 AI 工具已经开始支持直接读取这种标准文档来辅助生成代码了,文档写得好,AI 帮你干活儿都更利索。

4. 告别文档腐化:CI/CD集成与接口契约测试实战

打个比方,做开发最怕的就是“文档滞后”。代码早就改得面目全非了,结果 Swagger 页面上还挂着老的字段,前端同学对着文档调了半天,一跑就报错,这种锅背起来真的冤。这种情况在咱们圈子里叫“文档腐化”。

要解决这个问题,光靠开发人员的自觉性是不行的,人总会忘。咱们得靠流程,也就是把文档生成和校验嵌进 CI/CD 流水线里。

代码即文档,强制同步

现在的 OpenAPI 3.1.0(2021年2月发布,完全兼容 JSON Schema 2020-12)非常强调“注解驱动”。简单来说,就是代码写完,文档自动就出来了。咱们以 Spring Boot 项目为例,用现在最火的 springdoc-openapi 来演示。

假设我们有一个用户登录的接口,如果你不写注解,生成的文档就只有路径,参数说明全无。咱们得这么写:

package com.example.demo.controller; import com.example.demo.dto.LoginRequest; import com.example.demo.dto.LoginResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/v1/auth") @Tag(name = "认证管理", description = "用户登录、注册等相关接口") public class AuthController { @PostMapping("/login") @Operation(summary = "用户登录接口", description = "通过用户名和密码获取JWT令牌") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "登录成功", content = @Content(schema = @Schema(implementation = LoginResponse.class))), @ApiResponse(responseCode = "401", description = "用户名或密码错误", content = @Content(schema = @Schema(hidden = true))) }) public ResponseEntity<LoginResponse> login( @Parameter(description = "登录请求体", required = true) @RequestBody LoginRequest request) { // 模拟业务逻辑 if ("admin".equals(request.getUsername()) && "123456".equals(request.getPassword())) { LoginResponse response = new LoginResponse(); response.setToken("fake-jwt-token-12345"); response.setExpiresIn(3600); return ResponseEntity.ok(response); } return ResponseEntity.status(401).build(); } }

配合的 DTO 也要把注解加好,这样生成的 components 才完整:

package com.example.demo.dto; import io.swagger.v3.oas.annotations.media.Schema; public class LoginRequest { @Schema(description = "用户名", example = "admin", required = true) private String username; @Schema(description = "密码", example = "123456", required = true, format = "password") private String password; // Getters and Setters public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }

契约测试:守住API的底线

光有文档还不够,咱们得确保后端返回的数据结构没跑偏。这时候就要引入契约测试。咱们可以利用生成的 OpenAPI 文档作为“契约”,在测试阶段去校验接口返回是否符合规范。

这里推荐一个神器 rest-assured 结合 OpenAPI 解析。不过更简单的是直接用 openapi-diff 或者 swagger-request-validator

在 CI 流水线中,咱们可以加一个步骤:每次构建时,导出最新的 OpenAPI JSON,然后跑一遍测试,看接口是否违反了契约。

这里给个基于 swagger-request-validator 的测试示例,这玩意儿能直接拿线上的请求去对比文档:

package com.example.demo; import com.atlassian.oai.validator.OpenApiInteractionValidator; import com.atlassian.oai.validator.model.Request; import com.atlassian.oai.validator.model.Response; import com.atlassian.oai.validator.model.SimpleRequest; import com.atlassian.oai.validator.model.SimpleResponse; import com.atlassian.oai.validator.report.ValidationReport; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @SpringBootTest @AutoConfigureMockMvc public class ContractTest { @Autowired private MockMvc mockMvc; // 核心要点:这里加载的是咱们项目启动后生成的 OpenAPI 文档 // 实际项目中,你可以让 CI 先 curl 出 /v3/api-docs 保存为 json,然后再跑测试 private final OpenApiInteractionValidator validator = OpenApiInteractionValidator .createFor("openapi.yaml") // 假设你已经把文档生成到了 classpath 下 .build(); @Test public void validateLoginApiAgainstContract() throws Exception { // 模拟一个请求 String requestBody = "{\"username\": \"admin\", \"password\": \"123456\"}"; MvcResult result = mockMvc.perform(post("/api/v1/auth/login") .header("Content-Type", "application/json") .content(requestBody)) .andExpect(status().isOk()) .andReturn(); // 构造 Request 和 Response 给校验器 Request request = new SimpleRequest.Builder("POST", "/api/v1/auth/login") .withContentType("application/json") .build(); MockHttpServletResponse servletResponse = result.getResponse(); Response response = new SimpleResponse.Builder(servletResponse.getStatus()) .withContentType(servletResponse.getContentType()) .withBody(servletResponse.getContentAsString()) .build(); // 执行校验 ValidationReport report = validator.validate(request, response); // 如果有报错,这里会打印出来,比如缺字段、类型不对等 if (report.hasErrors()) { report.getMessages().forEach(msg -> System.out.println("契约校验失败: " + msg.getMessage())); } assertThat(report.hasErrors(), is(false)); } }

📖 学习建议:千万不要只在本地生成文档。一定要在 Jenkins 或 GitHub Actions 的 Pipeline 里加一个 Job,专门负责 mvn verify 或者 npm run generate-docs,然后把生成的 openapi.json 作为构建产物保存下来。这样谁改坏了接口,CI 直接挂红灯,想混过去都难。

---

5. 生产环境安全:禁用Swagger与防信息泄露配置

这事儿我必须得重点强调一下:很多新手甚至工作几年的老鸟,上线的时候压根不管 Swagger 的事儿,直接把 http://api.xxx.com/swagger-ui.html 暴露出去了。

这就好比你把家里的保险箱密码贴在门口,还画了个箭头指向卧室。Swagger 页面里不仅有接口路径,还有你定义的 DTO 结构、甚至有时候还有示例数据(比如真实的 Token 示例)。如果被不怀好意的人扫到了,那就等着被脱裤吧。

不同环境的动态配置

咱们的目标很明确:开发环境(Dev)和测试环境(Test)可以随便看,生产环境(Prod)必须关闭。

在 Spring Boot 中,最优雅的方式是利用 @Profile 或者配置文件条件。咱们看代码,这是最稳妥的写法。

首先,确保你的 pom.xml 里依赖的是 springdoc-openapi-starter-webmvc-ui

然后,咱们写一个配置类,通过 @Profile 来控制 Swagger 的行为:

package com.example.demo.config; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @Configuration public class OpenApiConfig { /** * 只在 dev 和 test 环境下激活这个 Bean * 生产环境没有这个 Bean,Swagger 就不会生效 */ @Bean @Profile({"dev", "test"}) public OpenAPI customOpenAPI() { return new OpenAPI() .info(new Info() .title("我的后端API文档") .version("v1.0.0") .description("仅用于开发和测试环境调试")); } }

但这还不够,即使没加载 OpenAPI Bean,springdoc 相关的静态资源可能还是能访问。咱们得彻底把 UI 的路径给堵上。

application-prod.yml 中,直接把 springdoc 相关的开关全部关掉:

# application-prod.yml springdoc: api-docs: enabled: false swagger-ui: enabled: false # 防止有人通过 actuator 或者其他方式访问

进阶:通过拦截器彻底封死

有时候配置文件不生效,或者你想更保险一点。咱们可以写个拦截器,凡是访问 /swagger* 或者 /v3/api-docs 的,直接返回 404 或者 403。

package com.example.demo.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SwaggerBlockInterceptor()) .addPathPatterns("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**"); } private static class SwaggerBlockInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 简单粗暴,直接拦截 response.setStatus(HttpServletResponse.SC_NOT_FOUND); // 或者返回 403 Forbidden // response.setStatus(HttpServletResponse.SC_FORBIDDEN); return false; // 终止请求 } } }

求职必备:如何防止泄露?

如果面试官问你“生产环境怎么处理 Swagger”,你别只说“删掉依赖”。那太 low 了,而且万一以后要调试还得加回来。

📖 学习建议:最佳实践是通过 Maven Profile 或者 Spring Profile 来控制依赖和作用域

如果是 Maven,你可以把 Swagger 的依赖 scope 设置为 provided 或者只在 dev 的 profile 里引入。

但是最推荐的还是我上面写的,利用 springdoc.swagger-ui.enabled=false 这个配置项。因为 OpenAPI 3.1.0 的规范支持非常完善,但安全配置一定要做在代码之外,通过环境变量或配置文件控制,这样最灵活,也最安全。

---

6. 未来趋势:AI辅助生成与AsyncAPI异步接口融合

咱们做技术的,眼光得放长远点。现在的 Swagger/OpenAPI 虽然好用,但也不是完美无缺。根据我最近在开发者社区看到的热门讨论,以及 2024 到 2026 年的技术趋势,有两个方向咱们必须得关注:AI 辅助生成异步接口(AsyncAPI)的融合

AI 辅助:从“写代码”到“说需求”

以前咱们写 API 文档,得一个注解一个注解地敲。现在不一样了,大语言模型(LLM)火了之后,已经有人开始尝试 AI 辅助生成 API 定义 了。

想象一下,你对着 AI 说:“我要一个用户注册的接口,需要邮箱、密码,返回用户ID和创建时间。”

AI 直接给你吐出一段符合 OpenAPI 3.1.0 规范的 YAML 代码。

openapi: 3.1.0 info: title: User Registration API version: 1.0.0 paths: /api/v1/users/register: post: summary: Register a new user requestBody: required: true content: application/json: schema: type: object properties: email: type: string format: email example: "user@example.com" password: type: string format: password example: "Str0ngP@ss!" required: - email - password responses: '201': description: User created successfully content: application/json: schema: type: object properties: userId: type: integer format: int64 example: 101 createdAt: type: string format: date-time

这比手写快多了吧?而且现在的趋势是,这种 AI 工具还能智能检测文档逻辑漏洞,比如你定义了 password 字段,但忘了加 minLength 或者 format: password,AI 可能会提示你增强安全性。这就是所谓的 安全左移(Shift Left Security),在写文档的时候就把坑填上。

AsyncAPI:搞定 WebSocket 和 Kafka

咱们现在的 Swagger(OpenAPI)主要管的是同步请求,也就是 HTTP 那一套。但现在的系统,微服务之间全靠消息队列(Kafka、RabbitMQ)或者 WebSocket 通信。

如果还是用 OpenAPI 去描述异步接口,那就太拧巴了。所以社区推出了 AsyncAPI

核心要点:AsyncAPI 的语法设计和 OpenAPI 非常像。如果你会写 Swagger,学 AsyncAPI 分分钟的事。

比如,你要描述一个 Kafka 的 Topic,以前你没法写在 Swagger 里,现在用 AsyncAPI 可以这么写(这是一个简单的示例):

asyncapi: 2.6.0 info: title: User Signup Service version: 1.0.0 channels: user/signedup: subscribe: summary: Receive information about user signup message: name: UserSignup contentType: application/json payload: type: object properties: userId: type: string format: uuid signedUpAt: type: string format: date-time

现在的趋势是,统一管理。大家希望在一个门户里,既能看到 RESTful 的同步接口,又能看到 MQ 的异步事件。虽然目前这两个规范还是独立的,但工具链正在融合。比如 OpenAPI GeneratorAsyncAPI Generator 的命令行参数都越来越像,甚至有些平台开始尝试在一个 UI 界面里同时渲染两种文档。

无代码与低代码的冲击

还有一个很有意思的趋势,就是 无代码/低代码平台 对 API 文档的依赖。

现在的低代码平台,很多都支持直接导入 OpenAPI 文档(特别是 3.0 或 3.1 版本),然后自动生成前端表单或者后端逻辑。这意味着,咱们写的 API 文档,未来可能不仅仅是给前端看,还是给低代码引擎“吃”的配置文件。

💡 经验总结:既然知道 AsyncAPI 是未来,我建议你现在做微服务设计的时候,尽量把 事件(Event)资源(Resource) 区分开。如果是事件驱动的部分,哪怕现在先不写 AsyncAPI 文档,也要在代码注释或者 README 里按照 AsyncAPI 的思维方式去描述消息结构。这样等以后工具链成熟了,你迁移起来就是分分钟的事,不用重构整个文档体系。