Generated Code¶
The IdempotencyModule declaration generates effect methods and DI registration.
What Gets Generated¶
Given:
[EffectsModule(typeof(IIdempotencyStore))]
[ServiceRegistration(nameof(AddDefaultIdempotency))]
public static partial class IdempotencyModule
{
public static void AddDefaultIdempotency(this IServiceCollection services)
{
services.TryAddSingleton<IIdempotencyStore, InMemoryIdempotencyStore>();
}
}
Effect Methods¶
public static partial class IdempotencyModule
{
public static partial class Idempotency
{
public static Eff<RT, bool> TryClaimAsync<RT>(string key, TimeSpan? expiry = null)
where RT : IHasIdempotencyStore => ...
public static Eff<RT, bool> IsClaimedAsync<RT>(string key)
where RT : IHasIdempotencyStore => ...
public static Eff<RT, Unit> ReleaseAsync<RT>(string key)
where RT : IHasIdempotencyStore => ...
public static Eff<RT, CachedResponse?> TryGetCachedResponseAsync<RT>(string key)
where RT : IHasIdempotencyStore => ...
public static Eff<RT, Unit> StoreCachedResponseAsync<RT>(
string key, CachedResponse response, TimeSpan? expiry = null)
where RT : IHasIdempotencyStore => ...
}
}
Capability Interface¶
CachedResponse¶
public sealed record CachedResponse(int StatusCode, Dictionary<string, string> Headers, byte[] Body);
Captures the full HTTP response (status, headers, body) for replay on duplicate requests.
DI Registration¶
TryAddSingleton means production stores (Redis, database) registered first take priority. InMemoryIdempotencyStore uses ConcurrentDictionary with TTL-based expiry.
Using Without Effects¶
public class WebhookProcessor(IIdempotencyStore store)
{
public async Task ProcessAsync(string eventId, string payload)
{
if (!await store.TryClaimAsync(eventId, TimeSpan.FromHours(24)))
return; // Already processed
// Process the webhook...
}
}
For functional composition, see Effects Composition.