Files
health/docs/adr/ADR-003-checkable-interface.md
Rene Nochebuena e1b6b7ddd7 feat(health): initial stable release v0.9.0
HTTP health check handler with parallel goroutine-per-check execution, 5 s request-derived timeout, and two-level criticality (LevelCritical → 503, LevelDegraded → 200).

What's included:
- `Checkable` interface (HealthCheck / Name / Priority) and `Level` type with LevelCritical and LevelDegraded constants
- `NewHandler(logger, checks...)` returning http.Handler; runs all checks concurrently via buffered channel, returns JSON with per-component status and latency
- `ComponentStatus` and `Response` types for the JSON response body

Tested-via: todo-api POC integration
Reviewed-against: docs/adr/
2026-03-18 14:06:17 -06:00

2.4 KiB

ADR-003: Checkable Interface

Status: Accepted Date: 2026-03-18

Context

The health handler needs to interrogate arbitrary infrastructure components without knowing their concrete types. The options were:

  1. Pass func(ctx context.Context) error callbacks directly.
  2. Require a shared Checkable interface that infrastructure modules must implement.
  3. Accept an external registry where components register themselves by name.

The health module also needs a way to know what to call a component in the JSON output (name) and how to treat its failure (priority). Without these pieces of metadata, every caller would have to pass them as separate arguments alongside the check function.

Decision

A Checkable interface is defined in the health package with three methods:

type Checkable interface {
    HealthCheck(ctx context.Context) error
    Name() string
    Priority() Level
}

Infrastructure modules (postgres, mysql, etc.) embed health.Checkable in their own Component interface and implement all three methods. The health package does not import any infrastructure module — the dependency flows inward only: infra → health.

Name() returns a stable string used as the JSON key in the components map. Priority() returns the Level value that governs the HTTP status code logic (ADR-002). HealthCheck(ctx) performs the actual probe (e.g., pool.Ping(ctx)).

The handler accepts ...Checkable as a variadic parameter, so callers can register zero or more components at construction time. No dynamic registration or remove-after-register is supported.

Consequences

  • Positive: Infrastructure components carry their own health metadata — no out-of-band registration with name strings and level constants at the call site.
  • Positive: Compile-time safety: if a component does not implement all three methods, the assignment var _ health.Checkable = myComponent{} fails.
  • Positive: The interface is minimal (three methods) and stable; adding a fourth method would be a breaking change and should be versioned.
  • Negative: Any new type that wants to participate in health checking must implement three methods, not just a single function. For trivial cases (one-off checks) this is more boilerplate than a bare function callback. However, the named interface is preferred because metadata (Name, Priority) cannot be forgotten.