Files
lab/go/projects/lyra/docs/plans/2026-10-16-lyra-architecture.md
labmem 3280c8a7bc docs(lyra): 记录技术选型与分层架构
- 记录 Gin、PostgreSQL、Squirrel、sqlx 和 Goose 选型
- 明确 handler、service、model 调用链及 API 职责边界
- 整理目标目录结构、待定事项与后续验证要求
2026-10-16 14:52:32 +08:00

8.6 KiB
Raw Permalink Blame History

Lyra 技术选型与架构讨论记录

日期:2026-10-16

状态:HTTP 路由、数据访问、数据库迁移工具与目标目录结构已确认(结构见 architecture.md);本文档方案已获文档层面批准,代码实施尚未批准。

已确认

  • Lyra 是个人 Go 后端,先承接 Polaris,未来承接其他项目接口。
  • Polaris 采用一人管理、匿名公开浏览的模式。
  • 已有服务器和 PostgreSQL;具体部署与连接配置尚未核实。
  • 数据访问选择 Squirrel + sqlx,PostgreSQL 驱动使用 pgx stdlib。
  • HTTP 路由选择 Gin;用户无明显接口风格偏好,结合文档与开发便利性作出选择。
  • 数据库迁移工具选择 Goose。
  • 本次确认仅涉及选型,不代表批准搭建代码或接入真实数据库。

数据访问约定

  • 固定查询直接写 SQL,动态查询使用 Squirrel,统一由 sqlx 执行。
  • Squirrel 使用 Dollar 占位符,普通值使用参数绑定,动态标识符采用服务端白名单。
  • 使用带 Context 的执行方法;需要原子性的业务操作使用事务。
  • 显式声明查询字段,处理可空值和 JOIN 列名冲突。
  • 不同时引入 sqlc 或 golang-migrate。
  • 已讨论的取舍:无代码生成步骤,但查询与结果映射主要在运行时校验;上游维护低频。

当前实现状态

  • Lyra 仅有 go.mod 与 docs/ 下的文档,尚无接口、数据库或业务代码。
  • Polaris 的 services 仍同步读取本地 JSON,未接入 Lyra。
  • 未进行数据库连接或数据库结构修改。

架构建议(尚未定案)

  • 一个 Go 服务;根据用户不希望按 Polaris 建立独立项目目录的偏好,调整为按技术职责组织代码,项目在 HTTP 路由上使用不同前缀。
  • 各项目使用独立 HTTP 前缀,共享配置、日志、连接池等基础设施。
  • PostgreSQL 可按项目划分 schema;这是组织边界,不等于权限隔离。
  • 不提前建设微服务、统一用户中心或通用 CRUD 框架。
  • 日志候选为标准库 slog,具体中间件与日志集成方式另行确认。

目录结构与边界(已确认)

2026-10-16 用户确认以下目标组织方式;同日确认将数据访问职责并入 model,不再设独立 internal/store 目录。完整结构记录见 architecture.md。确认的是结构与职责划分,不代表立即创建所有文件或占位目录,代码实施仍需另行批准。

lyra/
├── cmd/lyra/main.go
├── internal/
│   ├── api/
│   │   ├── links.go
│   │   ├── categories.go
│   │   └── settings.go
│   ├── config/
│   │   └── config.go
│   ├── postgres/
│   │   └── postgres.go
│   ├── router/
│   │   └── router.go
│   ├── handler/
│   │   └── links.go
│   ├── service/
│   │   ├── links.go
│   │   └── links_test.go
│   ├── model/
│   │   ├── link.go
│   │   ├── link_model.go
│   │   └── link_model_test.go
│   └── httpx/
│       └── response.go
├── migrations/
│   └── 00001_polaris_create_tables.sql
├── docs/plans/
├── README.md
├── go.mod
└── go.sum
  • cmd/lyra 负责启动、依赖组装、调用路由注册和退出清理,不放业务规则。
  • 用户提出统一管理接口请求与响应;推荐 internal/api,只放 HTTP 请求与响应 DTO 及其字段标签,按资源组织文件,不放 handler、业务逻辑或数据库查询。
  • internal/router 注册路由与项目路径前缀;handler 处理参数绑定、DTO 转换与响应输出;service 管理业务规则;model 承载数据结构定义与数据访问(Squirrel/SQL 构造、sqlx 执行与结果映射),驱动为 pgx stdlib。测试与被测文件同目录。
  • 树中 handler/service/model 仅列出链接资源示例;model/link.go 定义 Link 数据结构,model/link_model.go 定义数据访问与 SQL,model/link_model_test.go 测试查询,均为示例,实际文件随实现新增,不创建空占位目录或文件。
  • handler 引用 API DTO 并转换为业务输入或响应;service/model 不依赖 API DTO 包,API DTO 包不引用 handler/service/model,避免传输契约和实现相互耦合或循环引用。
  • 请求、响应与数据库模型职责分开,即使初期字段相近,也不默认直接把数据库模型暴露为响应;接受必要的显式转换代码。
  • 主调用链为 handler→service→model→PostgreSQL;service 负责业务规则及需要的事务协调,model 支持同一事务内的数据库操作,不将跨多次写入各自独立提交当成原子业务。
  • 上述文件是职责划分建议,不要求简单接口机械地走完整三层,也不按每张表或每种类型机械复制一套层级与文件。
  • HTTP 层可以依赖 Gin;service 和 model 使用标准 context.Context,不接收 gin.Context。
  • 移除 platform 和 modules 分类层;配置、连接和 HTTP 辅助直接位于 internal/config、internal/postgres、internal/httpx,减少无必要的路径深度。httpx 只在需要统一响应输出时实现,不提前抽象用户、设置、通用 CRUD 等业务。
  • go.sum 在引入依赖后由 Go 工具生成;测试文件随实际功能编写,不创建空占位文件。
  • 新项目可以在路由上增加独立前缀并复用上述技术层目录,不强制增加项目目录。不同领域的同名类型需要明确命名;文件名本身不提供 Go 包级符号隔离。
  • 路由前缀只区分 HTTP 入口,不自动隔离代码依赖、数据库或权限;业务所有权与鉴权仍需显式设计,不能因路径不同就假定隔离成立。
  • PostgreSQL 按项目划分 schema 仍为建议,尚未确认;schema 不自动提供权限隔离。

外部示例参考

  • 参考 https://github.com/vespeng/gin-pathway ,读取 main 分支 commit 67582d1。
  • 借鉴其薄启动入口、显式依赖组装与统一错误处理思路;曾考虑其按业务分组,后按用户偏好改为技术分层,不直接复制具体代码或全部依赖。
  • 该示例没有独立 API DTO 目录,部分数据库模型直接作为响应,本方案不沿用这一点。
  • 不沿用其启动时同步表结构的机制;Lyra 仍使用已确认的 Goose 管理迁移,具体执行方式待确认。
  • 不预建空 pkg、scripts、tests 等目录,不引入当前无需求的 Redis、ORM 或全局数据库单例。
  • internal/api 与根 api 都是可行布局,小写 api 是命名习惯;推荐 internal/api 是因为当前 DTO 仅供 Lyra 内部使用,而非 Go 语言强制要求。
  • 另参考 https://github.com/llmons/go-dianping ,读取 main 分支 commit cc7fbcf9:借鉴其独立 API 响应定义、技术分层与构造函数注入依赖;不照搬 ORM 生成层及缓存设施。
  • go-dianping 的部分请求与响应复用数据库模型,Lyra 的独立请求/响应 DTO 方案不沿用这一做法。

推荐迁移执行方式(待用户确认)

  • 起步采用 Goose CLI,在独立部署步骤中显式执行 SQL 迁移;Web 服务启动时不自动迁移。
  • 迁移成功后再启动或更新服务;迁移失败则停止后续发布,检查实际状态。
  • 同一个 Lyra 数据库初期使用一个 migrations 目录和统一版本序列,文件名标注所属模块;不让不同模块共用版本表却各自维护重复版本号。
  • 部署流程保证同一数据库的迁移串行执行;如以后存在多个迁移入口或并发发布,再选择带数据库锁的迁移执行入口,不假定事务本身能防止并发迁移。
  • 优先 SQL 迁移,非事务 DDL 单独设计;生产回退需要评估数据影响,不自动执行 Down。
  • 表结构迁移与 Polaris JSON 初次导入分开设计,不能把 Goose 等同于业务数据导入工具。

后续讨论顺序

  1. 确认 Goose 的执行入口、串行执行或数据库锁方案(技术分层与路由前缀组织已确认,见 architecture.md)。
  2. 确认首阶段范围:建议先做 JSON 导入、只读 API 和 Polaris 接入;管理写入另行确定认证方式。
  3. 编写实施与验证方案,获得明确批准后再实施。

后续实施方案必须覆盖的验证

  • 使用隔离的 PostgreSQL 测试环境验证迁移、核心查询与事务,不默认操作现有真实数据。
  • 核对导入前后的分类、链接、排序和置顶语义。
  • 接入 Polaris 时验证 service 异步化、动态分类路由、缓存与后端故障处理。
  • 管理写接口上线前验证鉴权,不能以隐藏入口代替权限控制。