Reference App
Reference App
Section titled “Reference App”reference/standard-service is the canonical application shape in Plumego. Use it when the question is file layout, route ownership, and what is safe to copy into a real service.
If you have not run anything yet, start with Getting Started. If the question is ownership rather than service shape, use Repository Boundaries.
Directory map
Section titled “Directory map”reference/standard-service/ main.go internal/ app/ app.go routes.go config/ config.go handler/ api.go health.goWhat each file demonstrates
Section titled “What each file demonstrates”| File | Demonstrates | Copy this idea |
|---|---|---|
main.go | Load config, build app, register routes, start server | Keep bootstrap order explicit |
internal/app/app.go | Application-local construction | Wire dependencies through constructors |
internal/app/routes.go | Route registration | Keep one method, one path, one handler visible |
internal/config/config.go | Service-local config | Keep config parsing near the app, not in stable roots |
internal/handler/api.go | JSON API handlers | Use net/http signatures and contract write paths |
internal/handler/health.go | Liveness/readiness handlers | Keep health endpoints small and transport-only |
Assembly diagram
Section titled “Assembly diagram” 1 main.go load config, build app, register routes
2 internal/app core.New plus app-local dependencies
3 internal/config service-local config parsing
4 internal/handler net/http handlers and contract writes
5 boundaries ownership lives in docs/specs/tasks
1
Bootstrap
load config, build app, register routes
2
Construct
core.New plus app-local dependencies
3
Configure
service-local config parsing
4
Handle
net/http handlers and contract writes
5
Do not infer
ownership lives in docs/specs/tasks
main.go -> config.Load() -> app.New(cfg) -> core.New(cfg.Core, dependencies) -> local handlers and dependencies -> RegisterRoutes() -> /healthz -> /readyz -> /api/v1/greet -> Start()Key patterns from each file
Section titled “Key patterns from each file”main.go — load, build, start
Section titled “main.go — load, build, start”func main() { cfg := config.Load() // env → struct, explicit, no global app := app.New(cfg) // wire deps through constructors app.RegisterRoutes() // all routes in one place app.Start() // Prepare → ListenAndServe with signal handling}internal/app/app.go — explicit wiring
Section titled “internal/app/app.go — explicit wiring”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 — one method, one path, one handler
Section titled “internal/app/routes.go — one method, one path, one 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 — stdlib-compatible handler
Section titled “internal/handler/api.go — stdlib-compatible 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)}Copy or do not infer
Section titled “Copy or do not infer”| Safe to copy | Do not infer |
|---|---|
| Explicit bootstrap order | Every application must have identical internal packages |
| App-local constructors and route registration | reference owns repository-wide boundaries |
| Standard-library handler signature | x/* packages are part of the default path |
contract.WriteResponse and contract.WriteError | Feature-specific response helper families are not encouraged |
| Stable-root-only canonical path | Checked-in experimental extensions carry the same compatibility promise |
When to use this page
Section titled “When to use this page”| Situation | Use this page? |
|---|---|
| Starting a new Plumego service | Yes |
| Validating handler and route shape | Yes |
| Comparing service skeletons | Yes |
| Deciding which package owns a capability | No, use Modules Overview |
| Deciding compatibility or adoption posture | No, use Release Posture |
Read next
Section titled “Read next”| Next question | Page |
|---|---|
| How does a request flow after route registration? | Request Flow |
| Which surface owns a change? | Repository Boundaries |
| Which module should I open? | Modules Overview |
| Which package is safe to adopt? | Release Posture |