docs(httpserver): correct tier from 4 to 3
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.
This commit is contained in:
41
docs/adr/ADR-003-no-default-middleware.md
Normal file
41
docs/adr/ADR-003-no-default-middleware.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# ADR-003: No Middleware Installed by Default
|
||||
|
||||
**Status:** Accepted
|
||||
**Date:** 2026-03-18
|
||||
|
||||
## Context
|
||||
|
||||
Many HTTP frameworks and server libraries install a default middleware stack — request logging, panic recovery, CORS headers, request ID injection — on the assumption that most applications will want these. The question is whether `httpserver.New(...)` should do the same.
|
||||
|
||||
Arguments for a default stack:
|
||||
- Reduces boilerplate for the common case.
|
||||
- Ensures recovery from panics is always in place.
|
||||
|
||||
Arguments against:
|
||||
- Different applications need different middleware in different orders. Order matters: request ID must precede the logger so the logger can attach the ID; auth must precede RBAC; compression must follow auth.
|
||||
- Installing middleware that the caller neither requested nor knows about makes the system harder to reason about and test.
|
||||
- The micro-lib design principle is zero-surprise construction: `New()` produces a minimal, predictable value. Behavior is added explicitly.
|
||||
- A middleware like `RequestLogger` needs a logger argument that `httpserver` would have to accept on behalf of `httpmw`. This couples configuration surface areas that should be independent.
|
||||
|
||||
## Decision
|
||||
|
||||
`httpserver.New(...)` installs no middleware. The router returned from `New()` is a bare `chi.NewRouter()`. Middleware is composed explicitly by the caller using `WithMiddleware(...)`:
|
||||
|
||||
```go
|
||||
srv := httpserver.New(logger, cfg,
|
||||
httpserver.WithMiddleware(
|
||||
httpmw.RequestID(uuid.NewString),
|
||||
httpmw.Recover(),
|
||||
httpmw.RequestLogger(&logAdapter{logger}),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
`WithMiddleware` is a variadic functional option that appends middleware to an internal slice. In `OnInit()`, each middleware is applied to the router via `s.Router.Use(mw)` in registration order (first registered = outermost in the chain).
|
||||
|
||||
## Consequences
|
||||
|
||||
- Every application must explicitly compose its middleware stack. This is intentional: the stack is visible, ordered, and testable in the application source.
|
||||
- Panic recovery is not automatic. If a caller omits `httpmw.Recover()`, unhandled panics will crash the process. This is a deliberate trade-off — it makes the stack explicit and avoids silent behavior.
|
||||
- Adding middleware after `OnInit()` has run is permitted by chi but is not the intended usage pattern. Route registration and middleware composition should both happen in the `BeforeStart` hook (before `OnStart` binds the port).
|
||||
- The `WithMiddleware` option appends, so multiple calls accumulate: `WithMiddleware(a, b)` followed by `WithMiddleware(c)` results in `[a, b, c]` applied in that order.
|
||||
Reference in New Issue
Block a user