50 lines
2.0 KiB
Markdown
50 lines
2.0 KiB
Markdown
|
|
# 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:
|
||
|
|
|
||
|
|
```go
|
||
|
|
// 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).
|