<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>openapi on </title>
    <link>/tags/openapi/</link>
    <description>Recent content in openapi on </description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Wed, 06 May 2026 10:00:00 +0800</lastBuildDate><atom:link href="/tags/openapi/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Contract First 落地实践：工具栈、团队协作与我踩到的坑</title>
      <link>/posts/openapi-contract-first-workflow/</link>
      <pubDate>Wed, 06 May 2026 10:00:00 +0800</pubDate>
      
      <guid>/posts/openapi-contract-first-workflow/</guid>
      <description>Contract First（也叫 API First）的理念并不新鲜：在写任何代码之前，先把 API 的形状定义清楚，让前后端、移动端、第三方消费者都从这份约定出发并行开发。但这套思路在很多团队里一直停留在 PPT，原因往往不是不认可，而是落地成本比想象中高——谁来写 spec、风格谁来管、变更谁来通知、breaking change 谁来拦，每一个问题都可能让一个团队悄悄退回 Code First。
这篇是我自己在 Spring Boot + 前端栈（TypeScript / React 或 Angular）里反复试错后整理的实践笔记，关注的是怎么让 Contract First 跑起来，并尽量持续不退回老路，而不是介绍 OpenAPI 语法。
如果你只想看 JVM 团队怎么把 contract 做成可执行验证（Spring Cloud Contract、WireMock、CI Quality Gates），可以直接跳到《Java 项目怎么做 contract testing：一次 Spring Cloud Contract 实践》。本文聚焦在 spec、协作和治理这一层。
为什么坚持 Contract First？ Code First 在小项目里没什么问题：后端先写完接口，把 Swagger 文档丢出去，前端照着对接。问题出现在团队和系统变大之后：
文档总是滞后——后端改了实现却忘了同步 Swagger 注解，前端按旧文档调用，联调阶段才发现 前端只能等——接口没写完，前端只能压时间或自己 mock，等真接口出来再返工 跨团队沟通成本高——每加一个消费方（移动端、第三方、内部 BFF），就要重新解释一遍接口 breaking change 靠口头同步——后端&amp;quot;顺手&amp;quot;改了字段类型，下游调用才发现问题 Contract First 的核心价值，是让 API 契约成为单一事实来源（single source of truth）。spec 不是事后的文档，而是开发的起点：所有人从同一份 YAML 出发，前后端可以并行，breaking change 最好显式审批。我倾向把它理解成一种&amp;quot;用工程约束代替口头沟通&amp;quot;的解耦机制，对微服务、电商、支付这种跨团队跨系统的场景尤其有价值。</description>
    </item>
    
    <item>
      <title>API 与 event contract 兼容性保障：工具机制与正确用法</title>
      <link>/posts/api-event-contract-compatibility-qa-2026/</link>
      <pubDate>Sat, 25 Apr 2026 10:00:00 +0800</pubDate>
      
      <guid>/posts/api-event-contract-compatibility-qa-2026/</guid>
      <description>📦 本文基于的完整项目源码：meirongdev/shop
上一篇 微服务 contract 兼容性的五层防线：从 ArchUnit 到 japicmp 讨论了如何用 ArchUnit、japicmp、WireMock 和运行时 Deprecation 头构建基础安全带。本文聚焦更具体的问题：BFF 对外的 API、BFF→MS 和 MS→MS 的内部 API、以及 Kafka 事件，各自应该用什么工具保障 contract 兼容性，这些工具的工作机制是什么。
一、两类 contract，两种保障方式 Q：BFF 对外暴露的 API 和 BFF→MS、MS→MS 之间的内部 API，兼容性的保障方式应该一样吗？
在我的实践中不太一样。主要原因在于 source of truth 不同。
BFF 对外的 API（前端、移动端、三方调用方）采用的是 API-first 方式：先有 OpenAPI YAML，生产者的 controller 实现这份 YAML，消费者根据这份 YAML 生成客户端代码或手写调用。在这条链路里，YAML 就是权威来源。保障兼容性就是保障 YAML 文件本身不引入 breaking change。
BFF→MS 和 MS→MS 之间采用的是 code-first 方式：Java record 是 source of truth，没有提前写 spec，序列化层（Jackson）从 record 推导出 JSON 结构。保障兼容性就是保障 Java record 的 JSON 表现形式不引入 breaking change。</description>
    </item>
    
    <item>
      <title>微服务契约共享的 Tradeoff：从 Monorepo 到 Polyrepo，该共享到哪一步</title>
      <link>/posts/microservices-contract-sharing-polyrepo-tradeoffs/</link>
      <pubDate>Wed, 22 Apr 2026 00:30:00 +0800</pubDate>
      
      <guid>/posts/microservices-contract-sharing-polyrepo-tradeoffs/</guid>
      <description>📦 本文基于的完整项目源码：meirongdev/shop
先给结论 如果你只想先看我的当前结论，再决定要不要继续往下看，可以先看这一版：
我更倾向于共享 contracts，而不是共享 client 接口。 即使在 monorepo 里，我也会尽早把 shared client 下沉到各 caller 自己的 client/ 包。 如果未来要拆成 polyrepo，我目前更偏向“共享 contracts artifact + caller 自己写 client + 契约测试兜底”。 只有当你真的面临多语言 SDK、外部开放接口，或者已经有成熟的平台治理能力时，我才会认真考虑 spec-first + 代码生成。 换句话说，这篇文章真正要回答的不是“Client 接口写在哪”，而是：跨服务的边界，到底该共享“数据契约”，还是连“调用方式”也一起共享。
起点：一个具体的问题 先用一个具体例子，把问题落到地上。
在早期的 shop 项目里，BFF 调下游 domain service 的链路是这样的：
shared/shop-contracts/shop-contracts-*：按 domain 拆分的契约模块，只有路径常量（OrderApi.CART_LIST）和 DTO（record），不依赖 Spring，也不含任何业务逻辑。 shared/shop-clients：集中放所有 @HttpExchange 接口（OrderServiceClient、MarketplaceServiceClient &amp;hellip;），被 buyer-bff、seller-bff 一起依赖。 shared/shop-common/shop-starter-http-client：统一的 ShopHttpExchangeSupport，负责用 RestClient 构造代理、挂拦截器、传 trusted headers、做错误映射。 BFF 侧的写法大致是：
@Bean OrderServiceClient orderServiceClient(ShopHttpExchangeSupport support, BuyerClientProperties properties) { return support.</description>
    </item>
    
    <item>
      <title>从 OpenAPI 3.0 到 3.1：一次升级验证记录</title>
      <link>/posts/openapi-31-upgrade/</link>
      <pubDate>Fri, 10 Apr 2026 14:00:00 +0800</pubDate>
      
      <guid>/posts/openapi-31-upgrade/</guid>
      <description>📦 本文基于的完整项目源码：https://github.com/meirongdev/shop
🏷️ 当前文章对应的代码版本：openapi_3_1_upgrade
背景 我们的电商平台自启动以来，一直使用 OpenAPI 3.0.x 作为 API 文档规范。近期在整理 SpringDoc 2.8.9 和 JSON Schema 2020-12 工具链时，我顺手验证了一次 OpenAPI 3.1 升级的可行性，最后决定先在项目里试着切过去。
就这次实践看，升级过程主要是一行配置，没有涉及 Java 代码改动。下面更像是一份升级笔记：记录我为什么做这件事、哪些收益比较直接，以及哪些兼容性边界要先确认。
OpenAPI 3.1 的核心变化 OpenAPI 3.1 于 2021 年底发布（官方发布公告），虽然看起来只是小版本号 +1，但它底层做了一个根本性的改变：全面对齐 JSON Schema Draft 2020-12（JSON Schema 2020-12 规范）。
这不只是&amp;quot;换了一个标准&amp;quot;，更像是把 OpenAPI Schema 往 JSON Schema 工具链上靠近了一步。
变化一：nullable 语义重构 OpenAPI 3.0 的写法：
{ &amp;#34;type&amp;#34;: &amp;#34;string&amp;#34;, &amp;#34;nullable&amp;#34;: true } 这是一个 OpenAPI 自定义扩展字段。JSON Schema 标准中不存在 nullable，这意味着：
标准 JSON Schema 校验器不认识它 代码生成器需要专门处理 OpenAPI 特有的逻辑 与 JSON Schema 生态工具链不互通 OpenAPI 3.</description>
    </item>
    
    <item>
      <title>微服务架构下的 OpenAPI 聚合文档实践</title>
      <link>/posts/openapi-aggregation-in-microservice-architecture/</link>
      <pubDate>Fri, 10 Apr 2026 10:00:00 +0800</pubDate>
      
      <guid>/posts/openapi-aggregation-in-microservice-architecture/</guid>
      <description>📦 本文基于的完整项目源码：https://github.com/meirongdev/shop
🏷️ 当前文章对应的代码版本：main
背景 在一个典型的微服务架构中，每个服务都独立维护自己的 API 文档。随着服务数量增长，前端开发者和第三方集成方常常面临一个痛点：
我需要去哪里找某个接口的文档？
传统的解决方案是让开发者记住每个服务的地址，或者维护一个手动的聚合页面。但这些方案都存在明显的问题：容易过时、维护成本高、无法与 CI/CD 集成。
本文以一个云原生电商平台的实际案例，记录我们当前如何通过 Spring Cloud Gateway MVC + SpringDoc 维护一套相对省事的 OpenAPI 聚合方案。
架构概览 我们的平台采用 Gateway + Thin BFF + Domain Service 三层架构：
Client └→ api-gateway:8080 (Spring Cloud Gateway MVC, JWT validation, rate limiting) ├→ /auth/** → auth-server ├→ /buyer/** → buyer-portal (Kotlin + Thymeleaf SSR) ├→ /seller/** → seller-app (KMP WASM SPA) ├→ /api/buyer/** → buyer-bff (aggregates domain services) ├→ /api/seller/** → seller-bff (aggregates domain services) ├→ /api/loyalty/** → loyalty-service ├→ /api/activity/** → activity-service ├→ /api/webhook/** → webhook-service └→ /api/subscription/** → subscription-service 共包含：</description>
    </item>
    
  </channel>
</rss>
