Skip to content

Commit 464a4d9

Browse files
iammukeshmjarvis
andauthored
docs: Add XML documentation to BuildingBlocks APIs (#1201)
* docs: Add XML documentation to Caching module - ICacheService: Document all interface methods - CacheServiceExtensions: Document GetOrSet patterns - Extensions: Document AddHeroCaching DI registration - DistributedCacheService: Document implementation details - HybridCacheService: Document L1/L2 caching strategy Part of #1176 * docs: Add XML documentation to Core/Domain module - IEntity, BaseEntity, AggregateRoot: Entity and DDD base types - IDomainEvent, DomainEvent, IHasDomainEvents: Domain event patterns - IAuditableEntity: Audit metadata interface - ISoftDeletable: Soft delete support - IHasTenant: Multi-tenancy marker Generated by Codex CLI with --full-auto flag Part of #1176 * docs: Add XML documentation to Exceptions module * docs: Add XML documentation to Context module * docs: Add XML documentation to Common module * docs: Add XML documentation to Abstractions module * docs: Add XML documentation to Persistence core module * docs: Add XML documentation to Persistence specifications, pagination, and interceptors --------- Co-authored-by: jarvis <jarvis@codewithmukesh.com>
1 parent 6b44e1c commit 464a4d9

33 files changed

+640
-14
lines changed

src/BuildingBlocks/Caching/CacheServiceExtensions.cs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1-
namespace FSH.Framework.Caching;
1+
namespace FSH.Framework.Caching;
2+
3+
/// <summary>
4+
/// Extension methods for <see cref="ICacheService"/> providing cache-aside pattern implementations.
5+
/// </summary>
26
public static class CacheServiceExtensions
37
{
8+
/// <summary>
9+
/// Gets an item from cache, or sets it using the provided callback if not found.
10+
/// Implements the cache-aside pattern synchronously.
11+
/// </summary>
12+
/// <typeparam name="T">The type of the cached item.</typeparam>
13+
/// <param name="cache">The cache service instance.</param>
14+
/// <param name="key">The unique cache key.</param>
15+
/// <param name="getItemCallback">A callback function to retrieve the item if not in cache.</param>
16+
/// <param name="slidingExpiration">Optional sliding expiration for the cached item.</param>
17+
/// <returns>The cached item or the newly retrieved and cached item.</returns>
418
public static T? GetOrSet<T>(this ICacheService cache, string key, Func<T?> getItemCallback, TimeSpan? slidingExpiration = null)
519
{
620
ArgumentNullException.ThrowIfNull(cache);
@@ -23,6 +37,17 @@ public static class CacheServiceExtensions
2337
return value;
2438
}
2539

40+
/// <summary>
41+
/// Asynchronously gets an item from cache, or sets it using the provided task if not found.
42+
/// Implements the cache-aside pattern asynchronously.
43+
/// </summary>
44+
/// <typeparam name="T">The type of the cached item.</typeparam>
45+
/// <param name="cache">The cache service instance.</param>
46+
/// <param name="key">The unique cache key.</param>
47+
/// <param name="task">An async function to retrieve the item if not in cache.</param>
48+
/// <param name="slidingExpiration">Optional sliding expiration for the cached item.</param>
49+
/// <param name="cancellationToken">Cancellation token for the operation.</param>
50+
/// <returns>The cached item or the newly retrieved and cached item.</returns>
2651
public static async Task<T?> GetOrSetAsync<T>(this ICacheService cache, string key, Func<Task<T>> task, TimeSpan? slidingExpiration = null, CancellationToken cancellationToken = default)
2752
{
2853
ArgumentNullException.ThrowIfNull(cache);
@@ -44,4 +69,4 @@ public static class CacheServiceExtensions
4469

4570
return value;
4671
}
47-
}
72+
}

src/BuildingBlocks/Caching/DistributedCacheService.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
using Microsoft.Extensions.Caching.Distributed;
1+
using Microsoft.Extensions.Caching.Distributed;
22
using Microsoft.Extensions.Logging;
33
using Microsoft.Extensions.Options;
44
using System.Text;
55
using System.Text.Json;
66

77
namespace FSH.Framework.Caching;
8+
9+
/// <summary>
10+
/// Implementation of <see cref="ICacheService"/> using distributed cache (Redis or in-memory).
11+
/// Provides JSON serialization for cached objects with configurable expiration policies.
12+
/// </summary>
813
public sealed class DistributedCacheService : ICacheService
914
{
1015
private static readonly Encoding Utf8 = Encoding.UTF8;
@@ -14,6 +19,12 @@ public sealed class DistributedCacheService : ICacheService
1419
private readonly ILogger<DistributedCacheService> _logger;
1520
private readonly CachingOptions _opts;
1621

22+
/// <summary>
23+
/// Initializes a new instance of <see cref="DistributedCacheService"/>.
24+
/// </summary>
25+
/// <param name="cache">The underlying distributed cache implementation.</param>
26+
/// <param name="logger">Logger for cache operations.</param>
27+
/// <param name="opts">Caching configuration options.</param>
1728
public DistributedCacheService(
1829
IDistributedCache cache,
1930
ILogger<DistributedCacheService> logger,
@@ -26,6 +37,7 @@ public DistributedCacheService(
2637
_opts = opts.Value;
2738
}
2839

40+
/// <inheritdoc />
2941
public async Task<T?> GetItemAsync<T>(string key, CancellationToken ct = default)
3042
{
3143
key = Normalize(key);
@@ -42,6 +54,7 @@ public DistributedCacheService(
4254
}
4355
}
4456

57+
/// <inheritdoc />
4558
public async Task SetItemAsync<T>(string key, T value, TimeSpan? sliding = default, CancellationToken ct = default)
4659
{
4760
key = Normalize(key);
@@ -57,6 +70,7 @@ public async Task SetItemAsync<T>(string key, T value, TimeSpan? sliding = defau
5770
}
5871
}
5972

73+
/// <inheritdoc />
6074
public async Task RemoveItemAsync(string key, CancellationToken ct = default)
6175
{
6276
key = Normalize(key);
@@ -65,6 +79,7 @@ public async Task RemoveItemAsync(string key, CancellationToken ct = default)
6579
{ _logger.LogWarning(ex, "Cache remove failed for {Key}", key); }
6680
}
6781

82+
/// <inheritdoc />
6883
public async Task RefreshItemAsync(string key, CancellationToken ct = default)
6984
{
7085
key = Normalize(key);
@@ -76,11 +91,24 @@ public async Task RefreshItemAsync(string key, CancellationToken ct = default)
7691
catch (Exception ex) when (ex is not OperationCanceledException)
7792
{ _logger.LogWarning(ex, "Cache refresh failed for {Key}", key); }
7893
}
94+
95+
/// <inheritdoc />
7996
public T? GetItem<T>(string key) => GetItemAsync<T>(key).GetAwaiter().GetResult();
97+
98+
/// <inheritdoc />
8099
public void SetItem<T>(string key, T value, TimeSpan? sliding = default) => SetItemAsync(key, value, sliding).GetAwaiter().GetResult();
100+
101+
/// <inheritdoc />
81102
public void RemoveItem(string key) => RemoveItemAsync(key).GetAwaiter().GetResult();
103+
104+
/// <inheritdoc />
82105
public void RefreshItem(string key) => RefreshItemAsync(key).GetAwaiter().GetResult();
83106

107+
/// <summary>
108+
/// Builds cache entry options with configured expiration settings.
109+
/// </summary>
110+
/// <param name="sliding">Optional sliding expiration override.</param>
111+
/// <returns>Configured cache entry options.</returns>
84112
private DistributedCacheEntryOptions BuildEntryOptions(TimeSpan? sliding)
85113
{
86114
var o = new DistributedCacheEntryOptions();
@@ -96,6 +124,12 @@ private DistributedCacheEntryOptions BuildEntryOptions(TimeSpan? sliding)
96124
return o;
97125
}
98126

127+
/// <summary>
128+
/// Normalizes the cache key by applying the configured prefix.
129+
/// </summary>
130+
/// <param name="key">The original cache key.</param>
131+
/// <returns>The normalized key with prefix applied.</returns>
132+
/// <exception cref="ArgumentNullException">Thrown when key is null or whitespace.</exception>
99133
private string Normalize(string key)
100134
{
101135
if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key));

src/BuildingBlocks/Caching/Extensions.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
1-
using Microsoft.Extensions.Configuration;
1+
using Microsoft.Extensions.Configuration;
22
using Microsoft.Extensions.DependencyInjection;
33
using Microsoft.Extensions.Options;
44
using StackExchange.Redis;
55

66
namespace FSH.Framework.Caching;
77

8+
/// <summary>
9+
/// Extension methods for registering caching services in the dependency injection container.
10+
/// </summary>
811
public static class Extensions
912
{
13+
/// <summary>
14+
/// Adds FullStackHero caching services to the service collection.
15+
/// Configures a hybrid L1/L2 cache with in-memory (L1) and Redis or distributed memory (L2).
16+
/// </summary>
17+
/// <param name="services">The service collection to add caching services to.</param>
18+
/// <param name="configuration">The application configuration containing caching options.</param>
19+
/// <returns>The service collection for chaining.</returns>
20+
/// <remarks>
21+
/// If Redis connection string is configured in <see cref="CachingOptions"/>, Redis is used for L2 cache.
22+
/// Otherwise, falls back to in-memory distributed cache for L2.
23+
/// The <see cref="HybridCacheService"/> is registered to provide both sync and async cache operations.
24+
/// </remarks>
1025
public static IServiceCollection AddHeroCaching(this IServiceCollection services, IConfiguration configuration)
1126
{
1227
ArgumentNullException.ThrowIfNull(configuration);
@@ -47,4 +62,4 @@ public static IServiceCollection AddHeroCaching(this IServiceCollection services
4762

4863
return services;
4964
}
50-
}
65+
}

src/BuildingBlocks/Caching/HybridCacheService.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@
77

88
namespace FSH.Framework.Caching;
99

10+
/// <summary>
11+
/// A hybrid cache implementation combining L1 (in-memory) and L2 (distributed) caching.
12+
/// Provides fast local access with distributed cache backup for multi-instance scenarios.
13+
/// </summary>
14+
/// <remarks>
15+
/// The hybrid approach uses memory cache for fast L1 access and automatically populates
16+
/// it from the L2 distributed cache on cache misses. Write operations update both caches.
17+
/// Memory cache uses 80% of the distributed cache sliding expiration for faster refresh.
18+
/// </remarks>
1019
public sealed class HybridCacheService : ICacheService
1120
{
1221
private static readonly Encoding Utf8 = Encoding.UTF8;
@@ -17,6 +26,13 @@ public sealed class HybridCacheService : ICacheService
1726
private readonly ILogger<HybridCacheService> _logger;
1827
private readonly CachingOptions _opts;
1928

29+
/// <summary>
30+
/// Initializes a new instance of <see cref="HybridCacheService"/>.
31+
/// </summary>
32+
/// <param name="memoryCache">The L1 in-memory cache.</param>
33+
/// <param name="distributedCache">The L2 distributed cache (Redis or memory-based).</param>
34+
/// <param name="logger">Logger for cache operations.</param>
35+
/// <param name="opts">Caching configuration options.</param>
2036
public HybridCacheService(
2137
IMemoryCache memoryCache,
2238
IDistributedCache distributedCache,
@@ -31,6 +47,11 @@ public HybridCacheService(
3147
_opts = opts.Value;
3248
}
3349

50+
/// <inheritdoc />
51+
/// <remarks>
52+
/// First checks L1 memory cache, then falls back to L2 distributed cache.
53+
/// If found in L2, the item is automatically populated into L1 for subsequent fast access.
54+
/// </remarks>
3455
public async Task<T?> GetItemAsync<T>(string key, CancellationToken ct = default)
3556
{
3657
key = Normalize(key);
@@ -66,6 +87,10 @@ public HybridCacheService(
6687
}
6788
}
6889

90+
/// <inheritdoc />
91+
/// <remarks>
92+
/// Writes to both L1 memory cache and L2 distributed cache simultaneously.
93+
/// </remarks>
6994
public async Task SetItemAsync<T>(string key, T value, TimeSpan? sliding = default, CancellationToken ct = default)
7095
{
7196
key = Normalize(key);
@@ -86,6 +111,10 @@ public async Task SetItemAsync<T>(string key, T value, TimeSpan? sliding = defau
86111
}
87112
}
88113

114+
/// <inheritdoc />
115+
/// <remarks>
116+
/// Removes from both L1 memory cache and L2 distributed cache.
117+
/// </remarks>
89118
public async Task RemoveItemAsync(string key, CancellationToken ct = default)
90119
{
91120
key = Normalize(key);
@@ -102,6 +131,7 @@ public async Task RemoveItemAsync(string key, CancellationToken ct = default)
102131
}
103132
}
104133

134+
/// <inheritdoc />
105135
public async Task RefreshItemAsync(string key, CancellationToken ct = default)
106136
{
107137
key = Normalize(key);
@@ -116,11 +146,23 @@ public async Task RefreshItemAsync(string key, CancellationToken ct = default)
116146
}
117147
}
118148

149+
/// <inheritdoc />
119150
public T? GetItem<T>(string key) => GetItemAsync<T>(key).GetAwaiter().GetResult();
151+
152+
/// <inheritdoc />
120153
public void SetItem<T>(string key, T value, TimeSpan? sliding = default) => SetItemAsync(key, value, sliding).GetAwaiter().GetResult();
154+
155+
/// <inheritdoc />
121156
public void RemoveItem(string key) => RemoveItemAsync(key).GetAwaiter().GetResult();
157+
158+
/// <inheritdoc />
122159
public void RefreshItem(string key) => RefreshItemAsync(key).GetAwaiter().GetResult();
123160

161+
/// <summary>
162+
/// Builds distributed cache entry options with configured expiration settings.
163+
/// </summary>
164+
/// <param name="sliding">Optional sliding expiration override.</param>
165+
/// <returns>Configured distributed cache entry options.</returns>
124166
private DistributedCacheEntryOptions BuildDistributedEntryOptions(TimeSpan? sliding)
125167
{
126168
var o = new DistributedCacheEntryOptions();
@@ -136,6 +178,11 @@ private DistributedCacheEntryOptions BuildDistributedEntryOptions(TimeSpan? slid
136178
return o;
137179
}
138180

181+
/// <summary>
182+
/// Gets memory cache expiration options, set to 80% of distributed cache expiration
183+
/// for faster refresh cycles.
184+
/// </summary>
185+
/// <returns>Memory cache entry options with sliding expiration.</returns>
139186
private MemoryCacheEntryOptions GetMemoryCacheExpiration()
140187
{
141188
var options = new MemoryCacheEntryOptions();
@@ -147,6 +194,12 @@ private MemoryCacheEntryOptions GetMemoryCacheExpiration()
147194
return options;
148195
}
149196

197+
/// <summary>
198+
/// Normalizes the cache key by applying the configured prefix.
199+
/// </summary>
200+
/// <param name="key">The original cache key.</param>
201+
/// <returns>The normalized key with prefix applied.</returns>
202+
/// <exception cref="ArgumentNullException">Thrown when key is null or whitespace.</exception>
150203
private string Normalize(string key)
151204
{
152205
if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key));
Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,70 @@
1-
namespace FSH.Framework.Caching;
1+
namespace FSH.Framework.Caching;
2+
3+
/// <summary>
4+
/// Provides caching operations for storing and retrieving items from cache.
5+
/// Supports both synchronous and asynchronous operations.
6+
/// </summary>
27
public interface ICacheService
38
{
9+
/// <summary>
10+
/// Asynchronously retrieves an item from the cache.
11+
/// </summary>
12+
/// <typeparam name="T">The type of the cached item.</typeparam>
13+
/// <param name="key">The unique cache key.</param>
14+
/// <param name="ct">Cancellation token for the operation.</param>
15+
/// <returns>The cached item if found; otherwise, null.</returns>
416
Task<T?> GetItemAsync<T>(string key, CancellationToken ct = default);
17+
18+
/// <summary>
19+
/// Asynchronously stores an item in the cache.
20+
/// </summary>
21+
/// <typeparam name="T">The type of the item to cache.</typeparam>
22+
/// <param name="key">The unique cache key.</param>
23+
/// <param name="value">The value to cache.</param>
24+
/// <param name="sliding">Optional sliding expiration. Uses default if not specified.</param>
25+
/// <param name="ct">Cancellation token for the operation.</param>
526
Task SetItemAsync<T>(string key, T value, TimeSpan? sliding = default, CancellationToken ct = default);
27+
28+
/// <summary>
29+
/// Asynchronously removes an item from the cache.
30+
/// </summary>
31+
/// <param name="key">The unique cache key to remove.</param>
32+
/// <param name="ct">Cancellation token for the operation.</param>
633
Task RemoveItemAsync(string key, CancellationToken ct = default);
34+
35+
/// <summary>
36+
/// Asynchronously refreshes the sliding expiration of a cached item.
37+
/// </summary>
38+
/// <param name="key">The unique cache key to refresh.</param>
39+
/// <param name="ct">Cancellation token for the operation.</param>
740
Task RefreshItemAsync(string key, CancellationToken ct = default);
41+
42+
/// <summary>
43+
/// Retrieves an item from the cache synchronously.
44+
/// </summary>
45+
/// <typeparam name="T">The type of the cached item.</typeparam>
46+
/// <param name="key">The unique cache key.</param>
47+
/// <returns>The cached item if found; otherwise, null.</returns>
848
T? GetItem<T>(string key);
49+
50+
/// <summary>
51+
/// Stores an item in the cache synchronously.
52+
/// </summary>
53+
/// <typeparam name="T">The type of the item to cache.</typeparam>
54+
/// <param name="key">The unique cache key.</param>
55+
/// <param name="value">The value to cache.</param>
56+
/// <param name="sliding">Optional sliding expiration. Uses default if not specified.</param>
957
void SetItem<T>(string key, T value, TimeSpan? sliding = default);
58+
59+
/// <summary>
60+
/// Removes an item from the cache synchronously.
61+
/// </summary>
62+
/// <param name="key">The unique cache key to remove.</param>
1063
void RemoveItem(string key);
64+
65+
/// <summary>
66+
/// Refreshes the sliding expiration of a cached item synchronously.
67+
/// </summary>
68+
/// <param name="key">The unique cache key to refresh.</param>
1169
void RefreshItem(string key);
12-
}
70+
}

0 commit comments

Comments
 (0)