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:
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 |
Related¶
- CQRS — Command/Query pattern
- Architecture — System design