Skip to content

Caching Pattern

Response caching through MediatR pipeline.

Overview

Queries implementing ICacheable are automatically cached:

flowchart TB
    Q[Query with ICacheable] --> C{Cache?}
    C -->|Hit| R1[Return Cached]
    C -->|Miss| H1[Handler]
    H1 --> S[Store in Cache]
    S --> R2[Return Fresh]

    CMD[Command with ICacheInvalidator] --> H2[Handler]
    H2 --> I[Invalidate Keys]
    I -.->|Clears| C

Making a Query Cacheable

public sealed record GetCategoriesQuery
    : IRequest<List<CategoryResponse>>, IRequestWithGroupId, ICacheable
{
    public Guid GroupId { get; set; }

    public string CacheKey => CacheConstants.Categories.List(GroupId);
    public TimeSpan CacheDuration => CacheConstants.DefaultDuration;
}

Cache Keys

Define cache keys in CacheConstants:

public static class CacheConstants
{
    public static readonly TimeSpan DefaultDuration = TimeSpan.FromMinutes(5);

    public static class Categories
    {
        public static string List(Guid groupId) => $"categories:list:{groupId}";
    }

    public static class Markers
    {
        public static string List(Guid groupId) => $"markers:list:{groupId}";
        public static string Detail(Guid markerId) => $"markers:detail:{markerId}";
    }
}

Cache Invalidation

Commands implement ICacheInvalidator to clear related caches:

public sealed record CreateCategoryCommand(CreateCategoryRequest Request)
    : IRequest<CategoryResponse>, IRequestWithGroupId, ICacheInvalidator
{
    public Guid GroupId { get; set; }

    public IEnumerable<string> CacheKeysToInvalidate =>
    [
        CacheConstants.Categories.List(GroupId)
    ];
}

Group-Scoped Caching

Cache keys include GroupId to ensure data isolation:

// Group A's categories: "categories:list:guid-a"
// Group B's categories: "categories:list:guid-b"

Cache Provider

Uses IMemoryCache by default. For multi-instance deployments, switch to distributed cache (Redis).

When to Cache

Cache Don't Cache
List queries User-specific data
Reference data (categories) Frequently changing data
Expensive computations Small/fast queries