Files
valkey/docs/adr/ADR-001-native-valkey-client.md
Rene Nochebuena eda54153d6 feat(valkey): initial stable release v0.9.0
Valkey (Redis-compatible) client component with launcher lifecycle and health check integration.

What's included:
- Config with Addrs, Password, SelectDB, CacheSizeEachConn (env-driven)
- Provider interface exposing native valkey-go Client() directly (no wrapper)
- Component interface: launcher.Component + health.Checkable + Provider
- New(logger, cfg) constructor for lifecycle registration via lc.Append
- Health check via PING at LevelDegraded priority
- Graceful shutdown calling client.Close() in OnStop

Tested-via: todo-api POC integration
Reviewed-against: docs/adr/
2026-03-19 13:29:28 +00:00

2.1 KiB

ADR-001: Expose Native valkey.Client Without a Wrapper Layer

Status: Accepted Date: 2026-03-18

Context

Some infrastructure modules wrap their underlying client behind a custom interface that re-exports only the operations they anticipate callers will need. This approach has two failure modes:

  1. The wrapper becomes a bottleneck: every new operation requires a new method on the wrapper interface, creating churn.
  2. The wrapper diverges from the upstream API surface, forcing callers to learn two APIs.

Valkey (and the compatible Redis protocol) has a rich, evolving command set. A thin wrapper that re-exports commands one at a time would either be incomplete or grow unboundedly.

Decision

The Component interface exposes the native vk.Client directly via Client() vk.Client. Callers receive a vk.Client value and use the valkey-go command builder API directly:

cmd := vkClient.B().Set().Key(key).Value(val).Ex(ttl).Build()
err  = vkClient.Do(ctx, cmd).Error()

The Provider interface is the minimal consumer-facing surface:

type Provider interface {
    Client() vk.Client
}

This module's only responsibilities are: constructing the client from Config, verifying connectivity on OnStart, issuing a PING for health checks, and closing the client on OnStop. All command execution is delegated entirely to the caller via the native client.

Consequences

Positive:

  • Callers have access to the full valkey-go API with no intermediary layer.
  • No wrapper code to maintain as the valkey-go API evolves.
  • The module stays small and focused on lifecycle management.
  • Optional client-side caching (CacheSizeEachConn) is supported by passing the config option through to vk.NewClient — no wrapper changes needed.

Negative:

  • Callers are coupled to the valkey-go library's API directly. Switching to a different Valkey/Redis client would require changes at every call site.
  • Mocking in tests requires either an httptest-style server or a mock that satisfies the vk.Client interface, which is more complex than mocking a minimal custom interface.