httpserver depends on launcher (Tier 2), placing it at Tier 3. With launcher corrected from Tier 5 to Tier 2, httpserver's tier drops accordingly.
2.8 KiB
ADR-002: Embedded chi.Router in HttpServerComponent
Status: Accepted Date: 2026-03-18
Context
HttpServerComponent must expose routing methods to callers so they can register routes before the server starts. There are two design options:
- Wrapper methods — define
Get(pattern, handler),Post(...),Route(...),Mount(...),Use(...), etc. onHttpServerComponentexplicitly, delegating each to an internal router. - Embedded router — embed
chi.Routerdirectly in the interface and the concrete struct, so all chi routing methods are promoted to the component surface automatically.
The application bootstrap pattern in this project registers routes in a lc.BeforeStart(...) hook, after db.OnInit has run but before srv.OnStart binds the port. This means the same value returned by httpserver.New(...) is used both as a launcher.Component (lifecycle) and as a router (route registration). Callers write:
srv := httpserver.New(logger, cfg, httpserver.WithMiddleware(...))
lc.Append(db, srv)
lc.BeforeStart(func() error {
srv.Get("/health", healthHandler)
srv.Route("/api/v1", func(r chi.Router) { ... })
return nil
})
Decision
Embed chi.Router directly in both the HttpServerComponent interface and the httpServer concrete struct. The interface is declared as:
type HttpServerComponent interface {
launcher.Component
chi.Router
}
The struct embeds chi.Router as an anonymous field, initialized to chi.NewRouter() in New().
This means callers receive the complete chi routing API — Get, Post, Put, Delete, Patch, Head, Options, Route, Mount, Use, With, Group, Handle, HandleFunc, Method, MethodFunc, Connect, Trace, NotFound, MethodNotAllowed — without any wrapper boilerplate and without any risk of an incomplete wrapper missing a method.
The compliance test (compliance_test.go) asserts at compile time:
var _ launcher.Component = httpserver.New(...)
var _ chi.Router = httpserver.New(...)
Consequences
chiis a visible part of theHttpServerComponentAPI. Callers usingRoute(...)orMount(...)must importgithub.com/go-chi/chi/v5for the callback type. This is intentional and explicit, not a leaky abstraction — httpserver is documented as chi-backed.- A future swap to a different router would be a breaking change to
HttpServerComponent. This is accepted: the router choice is a deliberate, long-term decision (see ADR-001). - Writing wrapper methods would have to be updated every time chi adds a new method. Embedding avoids that maintenance burden permanently.
- Middleware is applied to the embedded router in
OnInit()by callings.Router.Use(mw)for each registered middleware, preserving chi's standard middleware chaining semantics.