From 3280c8a7bc25a4fda5370c6ca33ad08ad9b5b04d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E8=82=B2=E6=96=B0?= <2635131263@qq.com> Date: Fri, 16 Oct 2026 14:52:32 +0800 Subject: [PATCH] =?UTF-8?q?docs(lyra):=20=E8=AE=B0=E5=BD=95=E6=8A=80?= =?UTF-8?q?=E6=9C=AF=E9=80=89=E5=9E=8B=E4=B8=8E=E5=88=86=E5=B1=82=E6=9E=B6?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 记录 Gin、PostgreSQL、Squirrel、sqlx 和 Goose 选型 - 明确 handler、service、model 调用链及 API 职责边界 - 整理目标目录结构、待定事项与后续验证要求 --- go/projects/lyra/docs/architecture.md | 71 ++++++++++ .../plans/2026-10-16-lyra-architecture.md | 123 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 go/projects/lyra/docs/architecture.md create mode 100644 go/projects/lyra/docs/plans/2026-10-16-lyra-architecture.md diff --git a/go/projects/lyra/docs/architecture.md b/go/projects/lyra/docs/architecture.md new file mode 100644 index 0000000..039d0ec --- /dev/null +++ b/go/projects/lyra/docs/architecture.md @@ -0,0 +1,71 @@ +# Lyra 目标结构 + +日期:2026-10-16 + +本文记录已确认的目标目录结构与职责边界。Lyra 当前仅有 go.mod 和文档,代码尚未实施;以下文件为计划形态,不代表已存在。选型与讨论过程见 [plans/2026-10-16-lyra-architecture.md](plans/2026-10-16-lyra-architecture.md)。 + +## 已确认技术栈 + +Gin(HTTP 路由)、PostgreSQL、Squirrel + sqlx(数据访问,pgx stdlib 驱动)、Goose(数据库迁移)。 + +## 目录结构 + +```text +lyra/ +├── cmd/lyra/main.go # 启动、依赖组装、退出清理 +├── internal/ +│ ├── api/ # HTTP 请求/响应 DTO,按资源分文件 +│ │ ├── links.go # links 资源示例(下同) +│ │ ├── categories.go +│ │ └── settings.go +│ ├── config/ +│ │ └── config.go # 配置定义与加载 +│ ├── postgres/ +│ │ └── postgres.go # 数据库连接 +│ ├── router/ +│ │ └── router.go # 路由注册、项目路径前缀 +│ ├── handler/ +│ │ └── links.go # 参数绑定、DTO 转换、响应输出 +│ ├── service/ +│ │ ├── links.go # 业务规则、事务协调 +│ │ └── links_test.go # 测试与源文件同目录 +│ ├── model/ +│ │ ├── link.go # Link 数据结构定义 +│ │ ├── link_model.go # 数据访问对象:CRUD 与 SQL(示例) +│ │ └── link_model_test.go # 查询测试(示例) +│ └── httpx/ +│ └── response.go # 公共响应辅助,按需实现 +├── migrations/ # Goose SQL 迁移文件 +├── docs/ +│ ├── plans/ +│ └── architecture.md # 本文 +├── README.md +├── go.mod +└── go.sum # 引入依赖后由 Go 工具生成 +``` + +- 标注示例的文件展示一个资源在各层的文件分布;分类、设置等资源按同样方式在 api/handler/service/model 新增对应文件。model/link.go 定义 Link 数据结构,model/link_model.go 定义数据访问与 SQL,model/link_model_test.go 测试查询,均为示例,实际文件随实现新增。 +- 只创建实际需要的文件;不预建空目录或空测试文件,具体迁移文件名随首个迁移确定。 + +## 组织原则 + +- 不按业务项目建代码子目录(无 polaris 等项目目录);项目通过 `/api/polaris` 等路由前缀区分,代码按技术职责分组,包内文件按资源划分。 +- 不保留 platform/modules 分类层;配置在 internal/config,连接在 internal/postgres,HTTP 辅助在 internal/httpx。 +- 简单接口不强制走完整三层,也不按每张表机械复制层级。 + +## 层间边界 + +- 主调用链为 handler→service→model→PostgreSQL;service 负责业务规则及需要的事务协调,model 支持同一事务内的数据库操作,不将跨多次写入各自独立提交当成原子业务。 +- 请求/响应 DTO 集中在 internal/api,与内部模型分开;即使初期字段相近,也不默认把数据库模型直接暴露为响应,接受必要的显式转换代码。 +- handler 引用 API DTO 完成绑定与转换;service/model 不依赖 Gin,也不依赖 internal/api DTO;api 包不反向引用 handler/service/model,避免传输契约与实现耦合或循环引用。 +- model 承载数据结构定义与数据访问:Squirrel/SQL 构造、sqlx 执行与结果映射,驱动为 pgx stdlib;不是 ORM 自动持久化,也不要求每种类型机械建立一套结构。 +- service/model 使用标准 context.Context,不接收 gin.Context。 + +## 尚未确定 + +以下仍为计划中的推荐或待确认项,未定为已决: + +- 代码尚未实施,也未进行数据库连接或结构修改。 +- Goose 的部署执行入口与并发控制(串行执行或数据库锁)方案。 +- PostgreSQL 按项目划分 schema 的做法(组织边界,不等于权限隔离)。 +- 日志方案(候选标准库 slog)与中间件集成方式。 diff --git a/go/projects/lyra/docs/plans/2026-10-16-lyra-architecture.md b/go/projects/lyra/docs/plans/2026-10-16-lyra-architecture.md new file mode 100644 index 0000000..0800f09 --- /dev/null +++ b/go/projects/lyra/docs/plans/2026-10-16-lyra-architecture.md @@ -0,0 +1,123 @@ +# Lyra 技术选型与架构讨论记录 + +日期:2026-10-16 + +状态:HTTP 路由、数据访问、数据库迁移工具与目标目录结构已确认(结构见 [architecture.md](../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](../architecture.md)。确认的是结构与职责划分,不代表立即创建所有文件或占位目录,代码实施仍需另行批准。 + +```text +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](../architecture.md))。 +2. 确认首阶段范围:建议先做 JSON 导入、只读 API 和 Polaris 接入;管理写入另行确定认证方式。 +3. 编写实施与验证方案,获得明确批准后再实施。 + +## 后续实施方案必须覆盖的验证 + +- 使用隔离的 PostgreSQL 测试环境验证迁移、核心查询与事务,不默认操作现有真实数据。 +- 核对导入前后的分类、链接、排序和置顶语义。 +- 接入 Polaris 时验证 service 异步化、动态分类路由、缓存与后端故障处理。 +- 管理写接口上线前验证鉴权,不能以隐藏入口代替权限控制。