Files
valkey/docs/adr/ADR-002-no-serialization-helpers.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.0 KiB

ADR-002: No Serialisation Helpers — Callers Marshal/Unmarshal Themselves

Status: Accepted Date: 2026-03-18

Context

Cache and key-value store modules often provide convenience methods such as SetJSON(ctx, key, value, ttl) or GetJSON(ctx, key, &target) that handle JSON marshalling and unmarshalling internally. While convenient, this approach has drawbacks:

  • It encodes a single serialisation format (typically JSON) into the module's API, making it hard to use binary formats like protobuf or MessagePack for performance-sensitive paths.
  • It obscures marshalling errors, which can become hard to distinguish from network errors.
  • It requires the module to understand the caller's data types, coupling them together.
  • It adds dependencies (e.g. encoding/json) that are not needed for all callers.

Decision

The valkey module provides no serialisation helpers. It exposes only Client() vk.Client, and all marshal/unmarshal logic lives in the caller:

// caller marshals before writing
b, err := json.Marshal(myValue)
cmd := client.B().Set().Key(key).Value(string(b)).Ex(ttl).Build()
client.Do(ctx, cmd)

// caller unmarshals after reading
result := client.Do(ctx, client.B().Get().Key(key).Build())
b, err := result.AsBytes()
json.Unmarshal(b, &myValue)

This keeps the module at zero opinion on serialisation format, zero added dependencies beyond valkey-go, and zero abstraction cost.

Consequences

Positive:

  • Callers choose their own serialisation format with no module-level constraints.
  • The module has no encoding/decoding logic that needs testing or maintenance.
  • Binary formats, compressed payloads, and plain strings all work identically.

Negative:

  • Every caller that stores structured data must implement its own marshal/unmarshal boilerplate, typically in a repository or cache layer.
  • There is no built-in protection against storing data with an incompatible format (e.g. writing JSON and reading with a protobuf decoder).