External effects and reminders
An atomic event commit does not make a network call, email, or reminder update atomic with event storage. Troolio models such work as an ExternalEffectIntent tied to a committed operation, request identity, serialized batch hash, subscriber, provider, and payload hash.
On this page
Durable intent protocol
ExternalEffectDispatcher.RegisterAsync records a frozen intent after the event receipt is available. DispatchAsync calls an IIdempotentExternalEffectProvider with a stable key and records a terminal receipt. RecoverPendingAsync pages pending intents after restart. A provider must deduplicate by key for the full effect lifetime and return a terminal result only when the outcome is definitive; uncertain failures throw for retry.
IAtomicEventEffectIntentStore supports a stronger path where event commit and effect intent are part of the same transaction, with AtomicEventEffectTransaction validating the receipt. A host must choose storage, worker scheduling, provider credentials, retention, and monitoring. If an intent is recorded only after the event commit, the host must account for the gap between those steps.
Reminder adapter
ReminderEffectIntent describes register/update or unregister operations. ReminderDesiredStateEffectProvider requires a host that can preserve desired state and definitive superseded outcomes. A reminder is an external effect, so replayed event handlers must not directly mutate it.
Example: Register an effect intent
After the event transaction has a complete receipt, register an intent for an idempotent provider. A worker dispatches and records its terminal outcome separately.
public static Task<ExternalEffectIntent> RegisterEffect(
IExternalEffectIntentStore intents, IIdempotentExternalEffectProvider provider,
AtomicEventTransactionReceipt receipt, byte[] payload) =>
ExternalEffectDispatcher.RegisterAsync(intents, provider, receipt,
"send-email", "notification-handler", payload);