api-surface.md 4.52 KB

三层 API

xly 不是只有一个 API,而是有三层,分别由三个独立的 Spring Boot 服务承载,面向不同受众,也有不同的运行契约。任何集成讨论的第一步,都是先确认你在和哪一层对话。

层级 服务 Context path 受众
内部 xlyEntry /xlyEntry xly SPA 自身(BACK + FROUNT)。对外部调用方不是稳定契约。
外部 xlyApi /xlyApi 从外部调用 xly 的租户和集成方。数据驱动的公开 API。
入站 webhook xlyInterface /xlyInterface 将事件推送进 xly 的第三方系统。带 Swagger。

每个服务都会构建成自己的 WAR,并在自己的 JVM 中运行。它们不共享进程内状态;它们共享的是数据库。正是这个共享数据库让服务拆分成立:内部 API 写入的数据会自动被外部 API 读到,因为两者都连接同一个 schema。

为什么是三层,以及拆分的代价

每一层最初都是为了解答不同的问题:

  • 内部层很大(对所有元数据驱动模块做通用 CRUD)、易变(随框架变化)、且有意保持弱类型(SPA 决定要什么,服务端照元数据执行)。
  • 外部层是收敛后的接口(只暴露允许集成方使用的端点),按 sApiCode 做版本化,并用 bearer token 认证。
  • 入站 webhook 层接收来自第三方系统的不可信 body,并路由到 xly 处理器。Swagger UI 放在这里,因为这个受众最需要交互式文档。

这个拆分有真实成本,Wiki 不应该略过:

  • 需要部署、监控、锁定版本的 WAR 有三个。 一次发布必须协调 xlyEntryxlyApixlyInterface 的构建。版本不匹配时(例如 xlyEntry 引入了 schema 变化,但 xlyApi 还没跟上),问题通常会静默存在,直到某条调用路径撞上它。
  • 存在重复代码。 RequestAddParamUtilxlyPersist(供 xlyEntry 使用)和 xlyApi 中各有一份,几乎是 56 行 / 57 行的拷贝。InterfaceControllerxlyApixlyInterface 中也都存在,并且有重叠的 /interfaceDefine/callthirdparty/* 端点。让两边保持同步依赖运维纪律,不是编译期保证。
  • 没有共享 session。 在 BACK 登录的用户,在 xlyApi 中没有 session;外部调用方需要单独获取 bearer token。这对外部集成是正确的,但也意味着内部跨 WAR 调用如果发生,必须走公开 token 流程。
  • 三个 context path 就意味着三条反向代理规则。 BACK=:8597FROUNT=:8598 到具体 WAR 的映射在 nginx 配置中,不在本仓库里。代理配置错误是代码库本身捕捉不到的常见故障模式。

这些层当然也可以做成一个部署物,只在内部用包边界隔离;Spring Boot 支持这种做法。那样的好处是:一次构建、一套依赖、一套 session 逻辑,也不会有重复工具类。代价是:各层更难独立扩容,也更难只限制外部调用方而不影响 SPA。xly 选择了部署期隔离;Wiki 的职责是把这个选择牺牲了什么写清楚。

每一层在运行时长什么样

  • 内部层 — 见五键读取。一个端点(/business/getModelBysId)返回完整表单布局;另一个端点(/business/addUpdateDelBusinessData)写入元数据命名的任意表中的任意行。端点少,形状通用。
  • 外部层 — 大多数调用走 /api/invoke/{sApiCode}sApiCodesysapi 元数据表中的一行,定义 SQL 模板、参数、认证要求和目标。新的外部 API 是注册成数据,不是写成代码;这和框架对自身表单采用的数据驱动基本论点一致。
  • 入站 webhook 层/interfaceDefine/invoke/{interfaceInvoke} 接收载荷,查找元数据中的匹配处理器,运行配置好的 SQL 或存储过程。此外还有少量为特定伙伴准备的硬编码接收器(/Push/Pull/send/sendQw)。

下一步看哪里