{
  "slug": "Building-a-Redis-Backed-Cache-Layer-in-Go-with-Uber-FX----c10052c9b9ca",
  "title": "Building a Redis-Backed Cache Layer in Go with Uber FX 🧠",
  "subtitle": "Redis is a powerful tool for caching, session management, and ephemeral storage. But integrating it cleanly into a Go project — especially…",
  "excerpt": "Redis is a powerful tool for caching, session management, and ephemeral storage. But integrating it cleanly into a Go project — especially…",
  "date": "2025-10-17",
  "tags": [
    "Go",
    "Redis"
  ],
  "readingTime": "2 min",
  "url": "https://medium.com/@mobinshaterian/building-a-redis-backed-cache-layer-in-go-with-uber-fx-c10052c9b9ca",
  "hero": "https://cdn-images-1.medium.com/max/800/1*9HMsqQlmrJ6igypqAEQ8KA.png",
  "content": [
    {
      "type": "heading",
      "level": 2,
      "text": "Building a Redis-Backed Cache Layer in Go with Uber FX 🧠"
    },
    {
      "type": "paragraph",
      "html": "Redis is a powerful tool for caching, session management, and ephemeral storage. But integrating it cleanly into a Go project — especially one using Uber FX — requires thoughtful design. In this article, we’ll walk through how to build a flexible, idiomatic Redis-backed cache layer that supports any type, avoids unnecessary abstractions, and fits naturally into a modular Go architecture."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*9HMsqQlmrJ6igypqAEQ8KA.png",
      "alt": "Building a Redis-Backed Cache Layer in Go with Uber FX 🧠",
      "caption": "",
      "width": 1024,
      "height": 1024
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧱 Project Structure"
    },
    {
      "type": "paragraph",
      "html": "We start by organizing our cache logic under <code>internal/storage/cache</code>, keeping it clean and domain-agnostic:"
    },
    {
      "type": "code",
      "lang": "text",
      "code": "internal/\n├── storage/\n│   \n├── cache/\n│   \n│   \n├── client.go        // Redis connection setup\n│   \n│   \n├──\nstore.go         // Cache logic (set/get/delete)\n│   \n│   \n└── module.go        // Uber FX integration"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🔌 Redis Client Setup (client.go)"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package cacheimport \"github.com/redis/go-redis/v9\"func NewClient() *redis.Client {\n    return redis.NewClient(&redis.Options{\n        Addr: \"localhost:6379\", DB: 0,\n    })\n}"
    },
    {
      "type": "paragraph",
      "html": "This sets up a Redis client using the official <code>go-redis</code> library."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧩 Generic Cache Store (store.go)"
    },
    {
      "type": "paragraph",
      "html": "We define a <code>Store</code> type that can cache any serializable Go value using JSON:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package cache\nimport (\n\"context\"\n\"encoding/json\"\n\"strings\"\n\"time\"\n\"github.com/redis/go-redis/v9\"\n)\ntype Store struct {\n    client *redis.Client\n}\nfunc NewStore(client *redis.Client) *Store {\n    return &Store{\n        client: client\n    }\n}\nfunc (s *Store) Set(ctx context.Context, key string, value interface{\n}, ttl time.Duration) error {\n    data, err := json.Marshal(value)\n    if err != nil {\n        return err\n    }\n    return s.client.Set(ctx, key, data, ttl).Err()\n}\nfunc (s *Store) Get(ctx context.Context, key string, dest interface{\n}) error {\n    data, err := s.client.Get(ctx, key).Bytes()\n    if err != nil {\n        return err\n    }\n    return json.Unmarshal(data, dest)\n}\nfunc (s *Store) Delete(ctx context.Context, key string) error {\n    return s.client.Del(ctx, key).Err()\n}\nfunc (s *Store) Exists(ctx context.Context, key string) (bool, error) {\n    count, err := s.client.Exists(ctx, key).Result()\n    return count > 0, err\n}"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧠 Key Naming Helper"
    },
    {
      "type": "paragraph",
      "html": "To keep Redis keys consistent and domain-aligned, we use a simple helper:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "func Key(parts...string) string {\n    return strings.Join(parts, \":\")\n}"
    },
    {
      "type": "paragraph",
      "html": "Example:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "Key(\"product\", \"123\") // \"product:123\""
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧬 FX Integration (module.go)"
    },
    {
      "type": "paragraph",
      "html": "We wire the cache layer into Uber FX:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package cacheimport \"go.uber.org/fx\"var Module = fx.Options(\nfx.Provide(NewClient),\nfx.Provide(NewStore),)"
    },
    {
      "type": "paragraph",
      "html": "Then in <code>main.go</code>:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "fx.New(    cache.Module,    // other modules...)"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧪 Example Usage"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "type Product struct {\n    ID string\n    Name string\n    Price float64\n}\nvar p Productif err := store.Get(ctx, Key(\"product\", \"123\"), &p);\nerr == nil {\n    return p, nil\n}"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧾 Naming Strategy"
    },
    {
      "type": "paragraph",
      "html": "To avoid repetition like <code>cache.CacheStore</code>We renamed the folder and typed:"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "Folder: <code>kvstore</code>",
        "Type: <code>KVStore</code>"
      ]
    },
    {
      "type": "paragraph",
      "html": "Resulting usage:"
    },
    {
      "type": "code",
      "lang": "text",
      "code": "kvstore.KVStore"
    },
    {
      "type": "paragraph",
      "html": "This keeps naming clean, idiomatic, and extensible."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Git commit"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Go-simple"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "✅ Summary"
    },
    {
      "type": "paragraph",
      "html": "This Redis-backed cache layer supports:"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "Generic type-safe storage",
        "TTL control",
        "Domain-aligned key naming",
        "Clean FX lifecycle integration",
        "Idiomatic naming and modularity"
      ]
    },
    {
      "type": "paragraph",
      "html": "It’s ready for production and extensible for metrics, health checks, and pub/sub. Whether you’re caching products, sessions, or search results, this design keeps your Go code clean, maintainable, and scalable."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*ceQtsZ31JcDBRSq_UIVAkQ.png",
      "alt": "Building a Redis-Backed Cache Layer in Go with Uber FX 🧠",
      "caption": "",
      "width": 1106,
      "height": 668
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*BmRlaWXOVbQw59trQTpu8w.png",
      "alt": "Building a Redis-Backed Cache Layer in Go with Uber FX 🧠",
      "caption": "",
      "width": 1441,
      "height": 408
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🏁 Conclusion"
    },
    {
      "type": "paragraph",
      "html": "Designing a Redis-backed cache layer in Go doesn’t have to be complicated. By focusing on idiomatic patterns, modular structure, and Uber FX integration, we’ve built a system that’s clean, extensible, and production-ready. Whether you’re caching products, sessions, or search results, this approach gives you:"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "🔧 A flexible <code>Store</code> that handles any type via JSON",
        "🧠 Predictable key naming for domain clarity",
        "⚡️ TTL support and cache invalidation",
        "🧬 Seamless FX lifecycle wiring",
        "📦 Scalable structure for future modules"
      ]
    }
  ]
}
