- 注册 GET /hello,使用统一响应结构并添加路由回归测试 - 使用 lyra.toml 配置 Gin 模式与监听地址,处理启动错误 - 明确 handler/service 业务分包约定并同步文档
8.2 KiB
8.2 KiB
Lyra 目标结构
日期:2026-10-17
本文记录已确认的目标目录结构与职责边界,并与当前真实实现同步。目录树是目标结构,并非全部已存在:标注“待新增”的文件或目录尚未创建,当前已有 Go 代码的是 cmd/lyra、internal/{api,config,handler,httpx,router};internal/{model,postgres,service} 仅含 AGENTS.md(记录职责与依赖约束),暂不构成 Go 包。选型与讨论过程见 plans/2026-10-16-lyra-architecture.md;handler/service 业务分包的调整记录见 plans/2026-10-17-business-package-layout.md。
已确认技术栈
- 已引入:Gin v1.12.0(HTTP 路由)、Viper v1.21.0(配置读取,TOML)。
go.sum已由 Go 工具生成。 - 目标选型(选型已确认,尚未作为代码依赖引入):PostgreSQL、Squirrel + sqlx(pgx stdlib 驱动)、Goose(数据库迁移)。
当前进度
- 程序入口在 module 根目录以
go run ./cmd/lyra启动:按相对当前工作目录的etc/lyra.toml加载配置(Viper,mapstructure标签),已启动 HTTP 服务,监听地址与端口来自配置文件;换目录执行会找不到配置。 app.env当前直接传给gin.SetMode,取值仅debug/release/test(Gin 的合法值);没有 dev/prod 等环境映射。- 已注册
GET /hello:internal/router/router.go的RegisterRoutes将其指向handler.Hello,经httpx.OK输出api.Response;路由注册配有 httptest 回归测试,不监听实际端口、不连接数据库。 - 已定义公共响应契约:
api.Response(success/data/error)、api.ErrorInfo(code/message/debug)与错误码类型api.ErrorCode及其默认文案Message()。 - 已实现响应输出辅助
httpx.OK、httpx.Fail。 - 尚未实现:真实业务逻辑(
/hello只是 HTTP 通路示例)、数据库访问与迁移 SQL;配置校验、环境变量覆盖与默认值;ErrorInfo.Debug的填充与按环境开关。日志方案未定。
目录结构
lyra/
├── cmd/lyra/main.go # 启动、依赖组装、退出清理
├── internal/
│ ├── api/ # HTTP 请求/响应 DTO、错误码与响应契约(平铺,按资源分文件)
│ │ ├── response.go # Response/ErrorInfo 结构
│ │ ├── code.go # ErrorCode 与错误码常量、默认文案
│ │ ├── links.go # links 资源 DTO 示例(待新增)
│ │ ├── categories.go # 待新增
│ │ └── settings.go # 待新增
│ ├── config/
│ │ └── config.go # 配置定义与加载
│ ├── postgres/
│ │ └── postgres.go # 数据库连接(待新增)
│ ├── router/
│ │ └── router.go # 路由注册、项目路径前缀
│ ├── handler/
│ │ ├── hello.go # GET /hello 处理(服务级简单接口,平铺在根包)
│ │ ├── links.go # 参数绑定、DTO 转换、响应输出(待新增)
│ │ └── polaris/ # 按明确业务分包的示例(待新增,不预建空包)
│ ├── service/
│ │ ├── links.go # 业务规则、事务协调(待新增)
│ │ ├── links_test.go # 测试与源文件同目录(待新增)
│ │ └── polaris/ # 按明确业务分包的示例(待新增,不预建空包)
│ ├── model/
│ │ ├── link.go # Link 数据结构定义(待新增)
│ │ ├── link_model.go # 数据访问对象:CRUD 与 SQL(待新增)
│ │ └── link_model_test.go # 查询测试(待新增)
│ └── httpx/
│ └── response.go # 公共响应输出辅助(OK/Fail)
├── etc/
│ └── lyra.toml # 运行配置(App/HTTP)
├── migrations/ # Goose SQL 迁移文件(暂无迁移,仅 README)
├── docs/
│ ├── plans/
│ └── architecture.md # 本文
├── README.md
├── go.mod
└── go.sum # 已生成
- 标注“待新增”的文件只是职责分布示例,随实现按需新增,不预建空文件或空目录;
internal/各目录均以AGENTS.md记录职责与依赖约束。 - model 下的
link*.go展示一个资源在各层的文件分布;分类、设置等资源按同样方式在 api/handler/service/model 新增对应文件。具体迁移文件名随首个迁移确定。 handler/polaris/、service/polaris/仅示意业务分包的形态:实际分包与否随业务边界明确时决定,路由、api、model 不照搬同样的分包。
组织原则
- handler、service 可以按明确业务分包(如
polaris/),仅在业务边界清晰时新增;服务级简单接口(如hello)保留在各自根包内平铺,不强制分包。 - router、api、model 等其他层暂时平铺,不按业务项目建代码子目录;项目通过
/api/polaris等路由前缀区分,包内文件按资源划分。 - 公共基础能力全局唯一:配置在 internal/config,连接在 internal/postgres,HTTP 辅助在 internal/httpx,不随业务项目复制或按项目再拆。
- 不保留 platform/modules 分类层;简单接口不强制走完整三层,也不按每张表机械复制层级。
- 不预建空业务包,分包随实际业务代码一起落地。
层间边界
- 真实业务的主调用链为 handler→service→model→PostgreSQL;服务级简单接口可以不经过 service/model(当前
/hello即是)。service 负责业务规则及需要的事务协调,model 支持同一事务内的数据库操作,不将跨多次写入各自独立提交当成原子业务。 - 请求/响应 DTO 集中在 internal/api,与内部模型分开;即使初期字段相近,也不默认把数据库模型直接暴露为响应,接受必要的显式转换代码。
- handler 引用 API DTO 完成绑定与转换;service/model 不依赖 Gin,也不依赖 internal/api DTO;api 包不反向引用 handler/service/model,避免传输契约与实现耦合或循环引用。
- internal/api 拥有响应契约与业务错误码(
Response/ErrorInfo/ErrorCode);不依赖 Gin。 - internal/httpx 负责响应输出:
OK固定 HTTP 200;Fail接收 HTTP 状态码与业务错误码,message为空时回退错误码默认文案。HTTP 状态码表示传输/结果类别,ErrorCode表示自定义业务错误,两者分开。 - internal/router 负责把 handler 挂到路由上:当前注册
GET /hello指向handler.Hello,后续按业务项目路径前缀(如/api/polaris)扩展。 - 配置由入口
main加载后组装并传递:internal/config只负责配置定义与加载,运行配置来自etc/lyra.toml;当前app.env直接传给gin.SetMode(仅debug/release/test),HTTP 地址端口供启动使用,数据库相关配置尚未有消费方。 - model 承载数据结构定义与数据访问:Squirrel/SQL 构造、sqlx 执行与结果映射,驱动为 pgx stdlib;不是 ORM 自动持久化,也不要求每种类型机械建立一套结构。
- service/model 使用标准 context.Context,不接收 gin.Context。
尚未确定 / 待实现
以下均为计划中的推荐或待确认项,尚未实现,也未定为已决:
- 配置校验、环境变量覆盖与默认值:尚未实现(当前无任何校验、覆盖或默认值)。
ErrorInfo.Debug的填充与按环境(dev)开关:尚未实现,目前仅有字段。- 分页响应约定(如 data/meta 结构)与统一错误处理中间件:尚未引入,仅为历史讨论。
- Goose 的部署执行入口与并发控制(串行执行或数据库锁)方案。
- PostgreSQL 按项目划分 schema 的做法(组织边界,不等于权限隔离)。
- 日志方案(候选标准库 slog)与中间件集成方式。