docs(lyra): 记录技术选型与分层架构
- 记录 Gin、PostgreSQL、Squirrel、sqlx 和 Goose 选型 - 明确 handler、service、model 调用链及 API 职责边界 - 整理目标目录结构、待定事项与后续验证要求
This commit is contained in:
@@ -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)与中间件集成方式。
|
||||
@@ -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 异步化、动态分类路由、缓存与后端故障处理。
|
||||
- 管理写接口上线前验证鉴权,不能以隐藏入口代替权限控制。
|
||||
Reference in New Issue
Block a user