首页
/ Chat2DB Java Web Controller 分层契约:从 HTTP 入口到领域服务的职责边界与代码规范

Chat2DB Java Web Controller 分层契约:从 HTTP 入口到领域服务的职责边界与代码规范

2026-09-10 16:22:11作者:段琳惟

本篇技术指南以 Chat2DB 社区版仓库中的 Java Web Controller Contracts 契约为核心,系统讲解 chat2db-community-web 模块中 Web 控制器的命名链、职责边界、结果包装器与服务调用约束。读者将掌握一套可直接用于评审与编码实践的"瘦控制器"规范:如何让 HTTP 层只做路由、绑定与包装,如何用 <Domain><Object><Action> 语义链统一端到端命名,以及如何借助标准 Result Wrapper 与 domain-api 服务接口杜绝业务逻辑向 Web 层泄漏。

1. 契约定位:控制器只是 HTTP 边界的适配器

Chat2DB 社区版后端采用多模块 Maven 工程(见 chat2db-community-server/pom.xml),其中 chat2db-community-web 承载所有 HTTP 暴露面。该契约首先给 Web 控制器下了一个严格定义:

一个 Web 控制器只适配 HTTP 边界,不得包含业务规则。控制器通过服务接口调用业务能力,并通过标准结果包装器返回 HTTP 结果。业务决策、对象装配、外部依赖调用不允许散落在 Web 层。

契约适用的对象是 chat2db-community-web 中的 @RestController@Controller 端点。过滤器(Filter)、拦截器(Interceptor)、异常处理器(Exception Handler)、转换器(Converter)、适配器(Adapter)以及异步执行组件不视为控制器,但它们仍需遵守各自模块的边界与对象转换契约(分别见 java-module-boundaries.mdjava-object-converter-contracts.md)。

从源码结构看,chat2db-community-web 的控制器统一收敛在 web/api/controller 目录下,目前共约 40 个控制器,覆盖数据源、SQL、AI、CLI、MCP、任务、运维日志与系统配置等能力域——这正是下节命名链在真实工程中的落地形态。

2. 业务命名链:<Domain><Object><Action> 语义锚点

契约要求:从 HTTP 输入、业务服务到 HTTP 输出,全链路名称必须共享同一个语义锚点

<Domain><Object><Action>

2.1 三段式语义拆解

含义 示例
Domain 顶层业务域前缀 SysDbAiCliMcpTaskOpsPlugin
Object 被操作的业务对象或能力 DatasourceConnectionSqlModelChatPermission
Action 业务动作 CreateUpdateDeleteGetListExecuteTestImportExport

2.2 顶层域前缀语义表

前缀 含义
Sys 系统设置、账号、权限、OAuth、代理与运行时配置
Db 数据库连接、元数据、SQL、DDL/DML、表、视图、函数、存储过程、触发器及 Redis 数据操作
Ai AI 对话、模型、补全、RAG、嵌入与 AI 辅助 Schema 能力
Cli CLI 与无头(headless)能力
Mcp MCP 协议、工具、资源与授权
Task 导入/导出、异步任务与长时工作流
Ops 操作历史、审计、已保存查询与历史记录
Plugin 插件扩展能力

2.3 命名规则(10 条)

  1. 控制器命名为 <Domain><Object>Controller,例如 DbDatasourceControllerAiAiChatControllerSysSysPermissionController
  2. 控制器方法名只表达动作,使用 lowerCamel 形式,例如 createupdatedeletegetlistexecutetest
  3. Web 请求 DTO 命名为 <Domain><Object><Action>Request
  4. Web 动作响应 DTO 命名为 <Domain><Object><Action>Response
  5. 资源/视图类响应允许使用 <Domain><Object>Response(如 DbDatasourceResponseDbTableResponse),但仍必须带业务域前缀;
  6. 领域服务接口命名为 I<Domain><Object>ServiceI<Domain><Object><Capability>Service
  7. 服务实现命名为 <Domain><Object>ServiceImpl<Domain><Object><Capability>ServiceImpl
  8. 同一端点的 Request、Controller、Service、Impl、Response 名称必须暴露一致的 <Domain><Object><Action> 语义;
  9. 禁止使用无业务域限定的泛化名称,如 SysSystemControllerDbDataSourceControllerCreateRequestExecuteResponseManagerServiceConfigDTO
  10. RdbRedisDatabaseDataSourceTableViewFunctionProcedureTrigger 不是顶层业务域,它们归属于 Db 域;共享值对象可以放在 model 包下,但不得伪装成业务请求或响应。

2.4 契约示例代码

public class DbDatasourceController {

    public DataResult<DbDatasourceCreateResponse> create(@Valid @RequestBody DbDatasourceCreateRequest request) {
        return DataResult.of(dbDatasourceService.create(request));
    }
}

public interface IDbDatasourceService {

    DbDatasourceCreateResponse create(DbDatasourceCreateRequest dbDatasourceCreateRequest);
}

public class DbDatasourceServiceImpl implements IDbDatasourceService {
}

2.5 仓库中的真实落地与 legacy 迁移

对照源码可以发现一个值得注意的细节:契约明确把 DbDataSourceController 列为"不允许的泛化名称"(规则 9),而仓库的 DbDataSourceController.java 恰好沿用该历史命名,同时在 db 子包下还有 DbDatabaseControllerDbSqlControllerDbTableControllerDbDmlController 等符合 Db<Object>Controller 形态的类。结合契约第 8 节"legacy 名称需登记迁移"的要求,可以推断:仓库当前处于"新旧命名并存、逐步向规范迁移"的阶段——评审时应把 DbDataSourceController 这类命名识别为待迁移项,而不是新代码的仿效样板。

值得一提的是,虽然类名是 legacy 的,但其方法命名完全符合契约第 2 条的动作化要求listcreateupdatedeletequeryByIdpreConnectattachcloseimportChat2dbexportDataSource,全部只表达动作、不带业务前缀。

3. Controller 职责边界:能做什么、绝不能做什么

3.1 控制器允许做的事(5 项)

  1. 声明 HTTP 路由、HTTP 方法、请求绑定与响应类型;
  2. 通过 Bean Validation 注解(@Valid@Validated@NotNull)触发参数校验;
  3. 调用 Web 转换器完成 Web DTO 与领域请求/响应之间的转换;
  4. 调用 ai.chat2db.community.domain.api.service 下的服务接口——禁止在 web.api.service 下再造一层业务服务
  5. ActionResultDataResult<T>ListResult<T>WebPageResult<T> 或 CLI 专属的 CliResult<T> 包装服务结果。

3.2 控制器禁止做的事(7 项)

  1. 评估涉及权限、版本(edition)、环境、归属、存在性、默认补全、状态流转或级联操作的业务规则;
  2. 直接调用 GatewayUtilWorkspaceStorageWebFacade、适配器、任务管理器、存储实现、Mapper、Repository、实现类或其它业务实现;
  3. 直接读写文件、数据库连接、OSS、远程 HTTP 服务、线程池、全局配置或进程退出行为;
  4. 在端点方法内用 if/elsefor/whiletry/catch 编排业务工作流;
  5. new 创建业务对象、用连续 setter 拼装对象或手写对象转换;
  6. 捕获异常后返回空列表、空成功、默认对象或原始错误信息;
  7. 一个端点方法调用另一个端点方法,或把可复用的业务工作流藏在私有 helper 中。

3.3 多步骤编排的正确去处

当端点需要多步骤编排时,正确做法是:在 domain-api 服务接口上新增一个业务语义清晰的方法,把编排逻辑实现在该边界之下。HTTP、CLI 运行时、SSE、上传/下载、遗留 web-facade 桥接,都不是再造一个 web.api.service 层的理由——能力应落到已有或新增的 domain-api 服务接口后面。

AiChatController.java 为例,SSE 流式对话、附件解析、模型配置增删改查、会话历史查询等端点,全部直接委托给 IAiChatStreamServiceIAiAttachmentParseServiceIAiModelConfigServiceIAiChatHistoryService 等接口,端点体里只有"取当前用户 → 调服务 → 转 DTO → 包装返回"四步,没有任何业务 if/else 或 try/catch 编排。

4. 标准结果包装器:普通端点唯一的返回通道

4.1 返回类型速查表

场景 返回类型
无响应数据的操作 ActionResult
单个对象、字符串、布尔值或 ID DataResult<T>
列表 ListResult<T>
分页 WebPageResult<T>
CLI 运行时 HTTP API CliResult<T>

普通端点禁止返回裸对象、集合、字符串、布尔值、Map 或业务模型。

4.2 包装器的仓库实现

标准包装器统一实现在 chat2db-community-toolstools/wrapper/result 包下,包括 ActionResultDataResult<T>ListResult<T>ListResultWithTotalweb/WebPageResult<T>,CLI 专属的 CliResult<T> 则在 web/api/model/response/cli/CliResult.java

DataResult.java 为例,其字段结构清晰地支撑了"成功 + 数据 + 可追踪错误"的统一协议:

字段 类型 语义
success Boolean 是否成功,默认 TRUE
errorCode String 错误码
errorMessage String 错误消息
errorDetail String 错误详情
solutionLink String 解决方案链接
data T 业务数据
traceId String 链路追踪 ID

同时提供三类工厂方法:of(data) 成功装载数据、empty() 空成功、error(errorCode, errorMessage) 构造失败结果;hasData(...) 判空、map(mapper) 在保持 success/errorCode/traceId 不变的前提下转换数据类型。ActionResult 提供 isSuccess()fail(errorCode, errorMessage, errorDetail)ListResult<T> 提供 of(list)empty()error(...) 及流式 map。这些能力正是契约"控制器按服务结果选择包装器"的基础设施。

4.3 允许的例外(5 类,不得扩张)

  1. SSE/流式端点:可返回 SseEmitter,但事件、错误与完成语义必须由专属适配器或服务负责。例如 AiChatController.javaPOST /api/v3/ai/chat/stream 返回 SseEmitter,实现完全委托给 IAiChatStreamService<ChatRequest, SseEmitter>.stream(request)
  2. 文件下载:可返回 ResponseEntity<Resource> 或直接写 HttpServletResponse,但权限、归属、路径解析与资源读取必须放在服务接口之后;
  3. 接收 MultipartFile:属于 HTTP 绑定;上传后的存储、远程调用与授权必须放在服务接口之后(AiChatController.parseUploadedAttachment 即把 MultipartFile 直接交给 IAiAttachmentParseService.parseUpload);
  4. CLI 运行时端点:允许使用 CLI 专属的 CliResult<T> 包装器——CliResult.java 定义了 successdataerrorCliErrorResponse)、requestId 结构及 ok(...)/error(...) 工厂方法;
  5. @Controller 的 HTML 路由:可返回 Spring MVC 视图名 String,但页面选择、OAuth 回调、Cookie、重定向与 model 属性仍需放在服务接口之后。

契约同时强调:这些例外绝不能扩张为普通业务端点的裸返回

5. 服务调用边界:只认 domain-api 接口

5.1 核心原则

控制器只能通过 ai.chat2db.community.domain.api.service 下的 IxxxService 接口依赖业务能力。Web 模块不得定义或注入 ai.chat2db.community.web.api.service 下的 controller-service 包装类;当契约缺失时,应扩展 domain-api 而不是在 Web 层补一层。

5.2 允许的控制器依赖(4 类)

  1. domain-api 服务接口;
  2. Web 转换器(converters/convertors);
  3. HTTP 绑定类型:请求/响应 DTO、MultipartFileHttpServletResponse
  4. 标准结果包装器类型。

5.3 禁止的控制器依赖(5 类)

  1. *Impl 类、Mapper、Repository、DAO 与存储实现;
  2. ai.chat2db.community.web.api.service 下的任何接口或实现;
  3. GatewayUtilWorkspaceStorageWebFacade、任务管理器、Web 适配器及一切持有业务行为或外部调用的组件;
  4. ApplicationContext#getBean、反射、类名字符串等绕过服务接口的机制;
  5. 具体的 domain-core、storage、SPI 或 plugin 实现模块。

若遗留的 web facade 或适配器仍持有业务入口,应先把能力迁移到 domain-api 服务之后,控制器不得直接调用它,也不得把它藏进 Web 服务包装类。

5.4 仓库实证

domain-api 服务接口统一位于 domain/api/service,按业务域组织为 ai/cli/dashboard/db/file/mcp/ops/storage/sys/task/ 子包,与契约的顶层域划分一一对应。

控制器构造注入的依赖几乎全部是这些接口:DbDataSourceController.java 只注入 IDbDataSourceServiceIDbWorkspaceDataSourceServiceIDbDataSourceImportServiceDataSourceWebConverterSSHWebConverter 两个 Web 转换器;AiChatController.java 只注入 IAiChatStreamServiceIAiModelConfigServiceIAiChatHistoryServiceIAiAttachmentParseServiceIIdentityServiceChatConverter——没有出现任何 *Impl、Mapper 或仓储类。

6. 委托业务规则:这些逻辑永远不属于端点方法

以下 7 类行为必须下沉到服务层:

  1. 环境或版本判断:ConfigUtils.isDesktop()isCommunity()isRelease()
  2. 基于 Cookie、Header 或 Session 的用户、组织、租户、权限与归属决策;
  3. 对象存在性、归属、删除/更新授权判断;
  4. 驱动、模型、页码、Schema 等业务默认值;
  5. 调用 Gateway、AI、License、更新、OSS 或远程 HTTP 服务后的结果组装;
  6. 批量导入、导出、删除与级联清理循环;
  7. 外部依赖失败后的重试、降级、空结果或错误码决策。

最后一条底线:控制器可以根据服务结果选择包装器,但绝不允许重新解释业务结果。仓库中 AiChatController.listSessions 的做法是标准范式——通过 identityService.currentUserId() 取身份,把用户 ID 作为参数传给 aiChatHistoryService.listSessions(userId),身份与归属校验完全发生在服务内部。

7. DTO 与转换边界

  1. 控制器入参使用 Web 请求 DTO,而非 domain 或 storage 模型(明确的 legacy 兼容契约除外);
  2. 控制器出参使用标准包装器;新端点应返回 Web 响应 DTO;历史端点可以把既有 domain-api 模型作为包装器泛型暴露,但控制器不得拼装或修改它;
  3. Web DTO 与领域请求/响应之间的转换属于 chat2db-community-web 中的转换器(converter);
  4. 控制器禁止使用 BeanUtils.copyProperties、JSON 往返、ObjectMapper.convertValue 或手写 setter 做对象转换。像 xxxService.setXxx(...) 这样的薄委托不算对象转换,但服务实现仍需遵守 java-object-converter-contracts.md 的对象转换契约。

仓库中的转换器集中在 web/api/converter 包:DataSourceWebConverter(含 request2paramrequest2responseresponse2storagestorage2responsedatabaseDto2response 等方法)、SSHWebConverterChatConvertertoModelConfigParamattachment2responsesession2responsemessage2response 等)。从 DbDataSourceController.javacreate 端点可以看到完整链路:request → DataSourceWebConverter.request2response → response2storage → workspaceDataSourceService.createDataSource → storage2response → DataResult.of(...),一次 HTTP 请求中的全部对象形态转换都由转换器完成,端点方法内零手写装配。

8. 评审检查清单:9 项硬性指标

Web 控制器评审必须逐项验证:

  1. 控制器类名使用允许的顶层业务域前缀;legacy 名称已登记迁移;
  2. Web 请求/响应 DTO 使用允许的业务域前缀;legacy 名称已登记迁移;
  3. chat2db-community-web 的端点返回标准包装器或明确的允许例外;
  4. 控制器不直接依赖 GatewayUtilWorkspaceStorageWebFacade、适配器、存储、Mapper、Repository 或实现类;
  5. 控制器只注入 domain-api 服务接口、转换器、HTTP 绑定类型与结果包装器,不存在 web.api.service 业务包装层;
  6. 端点方法体不直接调用外部依赖、静态业务 facade、System.exit、JDBC 连接、OSS 客户端或任务管理器;
  7. 端点方法体不含可疑的业务编排、对象变更、业务对象构造或过长的代码;
  8. 端点方法不调用其它端点方法或私有控制器 helper;
  9. @RequestBody 端点通过 @Valid@Validated 触发 Bean Validation。

契约特别强调:即使表面类型满足上述规则,复杂 legacy 端点仍需人工评审。这是该契约与普通命名规范的本质区别——它审查的是行为边界而非静态签名,DbDataSourceController@GetMapping("/datasource/connect") 返回 ListResult<DatabaseResponse>@Valid @NotNull DataSourceAttachRequest 触发校验的写法,正是清单第 3、9 项的合规示范。

9. 契约体系的协同定位

java-web-controller-contracts.md 是 Chat2DB 社区版 spec/code/server 契约族的一员,与其余四份契约构成完整的后端实现约束体系:

契约文档 约束对象
java-interface-contracts.md 服务接口签名与语义契约
java-impl-contracts.md 服务实现层行为约束
java-module-boundaries.md 模块间依赖方向与边界
java-object-converter-contracts.md 对象转换器职责
java-web-controller-contracts.md Web 控制器职责边界(本文主题)
java-plugin-contracts.md 插件扩展能力契约

对开发者的实操价值在于:新增一个端点时,可以按"命名链 → 包装器 → 服务接口 → 转换器"四步自查——类名是否 DomainObjectController、方法名是否纯动作、返回是否标准包装器、依赖是否只剩 domain-api 接口与转换器;只要这四点成立,控制器大概率就是合规的"瘦控制器"。而在评审存量代码时,则应把 legacy 命名(如 DbDataSourceControllerSysSystemController)与复杂端点列为人工复核重点,优先推动其背后的业务能力下沉到 domain-api 服务,而不是在 Web 层修修补补。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
927
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.94 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
603
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
396
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
527