跳转到内容

参考应用

reference/standard-service 是 Plumego 的规范应用形态。当问题是文件布局、路由归属,以及哪些结构可以安全复制到真实服务时,看这页。

如果还没有运行过服务,请先看开始使用。如果问题已经变成归属判断,请看仓库边界

reference/standard-service/
main.go
internal/
app/
app.go
routes.go
config/
config.go
handler/
api.go
health.go
文件展示内容可以复制的思路
main.go加载配置、构建 app、注册路由、启动服务启动顺序保持显式
internal/app/app.go应用本地构造依赖通过 constructor wiring
internal/app/routes.go路由注册一个 method、一个 path、一个 handler 保持可见
internal/config/config.go服务本地配置配置解析靠近应用,不放进稳定根
internal/handler/api.goJSON API handler使用 net/http 签名和 contract 写回路径
internal/handler/health.go存活/就绪 handler健康端点保持小而传输层
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()
func main() {
cfg := config.Load() // env → struct,显式,无全局变量
app := app.New(cfg) // 通过 constructor 注入依赖
app.RegisterRoutes() // 所有路由集中在一处
app.Start() // Prepare → ListenAndServe + 信号处理
}
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)
}

可以安全复制不应推导
显式启动顺序每个应用都必须有完全相同的 internal 包
应用本地 constructor 和 路由注册reference 负责仓库级边界
标准库 handler 签名x/* 包属于默认路径
contract.WriteResponsecontract.WriteError不要为每个功能另起一套响应 helper
只依赖稳定根的规范路径已检入的实验扩展都拥有同等兼容性承诺
场景是否使用这页
新建 Plumego 服务
校验 handler 和 route 形态
对比服务骨架
判断能力由哪个包负责否,去看模块总览
判断兼容性或采用姿态否,去看发布策略
下一个问题页面
路由注册后请求如何流动?请求流程
一项改动由哪个表面负责?仓库边界
应该打开哪个模块?模块总览
哪个包可以放心采用?发布策略