参考应用
reference/standard-service 是 Plumego 的规范应用形态。当问题是文件布局、路由归属,以及哪些结构可以安全复制到真实服务时,看这页。
如果还没有运行过服务,请先看开始使用。如果问题已经变成归属判断,请看仓库边界。
reference/standard-service/ main.go internal/ app/ app.go routes.go config/ config.go handler/ api.go health.go每个文件展示什么
Section titled “每个文件展示什么”| 文件 | 展示内容 | 可以复制的思路 |
|---|---|---|
main.go | 加载配置、构建 app、注册路由、启动服务 | 启动顺序保持显式 |
internal/app/app.go | 应用本地构造 | 依赖通过 constructor wiring |
internal/app/routes.go | 路由注册 | 一个 method、一个 path、一个 handler 保持可见 |
internal/config/config.go | 服务本地配置 | 配置解析靠近应用,不放进稳定根 |
internal/handler/api.go | JSON API handler | 使用 net/http 签名和 contract 写回路径 |
internal/handler/health.go | 存活/就绪 handler | 健康端点保持小而传输层 |
1 main.go 加载配置、构建 app、注册路由
2 internal/app core.New 加应用本地依赖
3 internal/config 服务本地配置解析
4 internal/handler net/http handler 与 contract 写回
5 边界 归属规则在 docs/specs/tasks
1
启动入口
加载配置、构建 app、注册路由
2
构造应用
core.New 加应用本地依赖
3
加载配置
服务本地配置解析
4
处理请求
net/http handler 与 contract 写回
5
不要推导
归属规则在 docs/specs/tasks
main.go -> config.Load() -> app.New(cfg) -> core.New(cfg.Core, dependencies) -> 本地 handlers 和 dependencies -> RegisterRoutes() -> /healthz -> /readyz -> /api/v1/greet -> Start()各文件核心模式
Section titled “各文件核心模式”main.go — 加载、构建、启动
Section titled “main.go — 加载、构建、启动”func main() { cfg := config.Load() // env → struct,显式,无全局变量 app := app.New(cfg) // 通过 constructor 注入依赖 app.RegisterRoutes() // 所有路由集中在一处 app.Start() // Prepare → ListenAndServe + 信号处理}internal/app/app.go — 显式接线
Section titled “internal/app/app.go — 显式接线”type App struct { Core *core.App Handler *handler.APIHandler}
func New(cfg *config.Config) *App { logger := plog.NewLogger() coreApp := core.New(core.DefaultConfig(), core.AppDependencies{Logger: logger}) return &App{ Core: coreApp, Handler: handler.New(logger), }}internal/app/routes.go — 一个 method、一个 path、一个 handler
Section titled “internal/app/routes.go — 一个 method、一个 path、一个 handler”func (a *App) RegisterRoutes() { a.Core.Get("/healthz", http.HandlerFunc(a.Handler.Health.Live)) a.Core.Get("/readyz", http.HandlerFunc(a.Handler.Health.Ready)) a.Core.Get("/api/v1/greet", http.HandlerFunc(a.Handler.API.Greet))}internal/handler/api.go — 标准库兼容的 handler
Section titled “internal/handler/api.go — 标准库兼容的 handler”type APIHandler struct{ Logger log.Logger }
func (h APIHandler) Greet(w http.ResponseWriter, r *http.Request) { name := r.URL.Query().Get("name") if name == "" { _ = contract.WriteError(w, r, contract.NewErrorBuilder(). Type(contract.TypeRequired).Detail("field", "name").Build()) return } _ = contract.WriteResponse(w, r, http.StatusOK, map[string]string{"message": "hello, " + name}, nil)}可以复制什么,不应推导什么
Section titled “可以复制什么,不应推导什么”| 可以安全复制 | 不应推导 |
|---|---|
| 显式启动顺序 | 每个应用都必须有完全相同的 internal 包 |
| 应用本地 constructor 和 路由注册 | reference 负责仓库级边界 |
| 标准库 handler 签名 | x/* 包属于默认路径 |
contract.WriteResponse 和 contract.WriteError | 不要为每个功能另起一套响应 helper |
| 只依赖稳定根的规范路径 | 已检入的实验扩展都拥有同等兼容性承诺 |
什么时候使用这页
Section titled “什么时候使用这页”| 场景 | 是否使用这页 |
|---|---|
| 新建 Plumego 服务 | 是 |
| 校验 handler 和 route 形态 | 是 |
| 对比服务骨架 | 是 |
| 判断能力由哪个包负责 | 否,去看模块总览 |
| 判断兼容性或采用姿态 | 否,去看发布策略 |
| 下一个问题 | 页面 |
|---|---|
| 路由注册后请求如何流动? | 请求流程 |
| 一项改动由哪个表面负责? | 仓库边界 |
| 应该打开哪个模块? | 模块总览 |
| 哪个包可以放心采用? | 发布策略 |