Effects Composition¶
The IdempotencyModule is an [EffectsModule(typeof(IIdempotencyStore))] that generates Eff<RT, T> effect methods for every IIdempotencyStore method.
Capability Interface¶
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 => ...
}
}
Effect Pipeline Examples¶
Manual idempotency in an event handler¶
// Deduplicate webhook processing using the event's unique ID
var processWebhook =
from claimed in IdempotencyModule.Idempotency.TryClaimAsync<AppRuntime>(
$"webhook:{webhook.EventId}", TimeSpan.FromHours(24))
from _ in claimed
? ProcessEvent(webhook)
: unitEff // Already processed — skip silently
select unit;
Claim with error recovery¶
// If processing fails, release the key so it can be retried
var processWithRecovery =
from claimed in IdempotencyModule.Idempotency.TryClaimAsync<AppRuntime>(
key, TimeSpan.FromHours(1))
from _ in Guard<AppRuntime>(claimed, Error.New(409, "Already processing"))
from result in ProcessPayment(command)
| @catch(e =>
from release in IdempotencyModule.Idempotency.ReleaseAsync<AppRuntime>(key)
from fail in FailEff<AppRuntime, PaymentResult>(e)
select fail)
select result;
Compose with dispatch¶
// Check idempotency before executing a command
var idempotentCommand =
from claimed in IdempotencyModule.Idempotency.TryClaimAsync<AppRuntime>(
$"cmd:{cmd.RequestId}")
from result in claimed
? AppDispatch.Dispatch(cmd)
: from cached in IdempotencyModule.Idempotency.TryGetCachedResponseAsync<AppRuntime>(
$"cmd:{cmd.RequestId}")
select cached // Return previously cached result
select result;
Most idempotency is handled automatically by the middleware — use these effect methods for manual control in event handlers, background jobs, or integration event subscribers.