Interface IReportingDomainEventPublisher
Publishes a domain event and reports each handler's outcome instead of swallowing failures.
public interface IReportingDomainEventPublisher
- Extension Methods
Remarks
IDomainEventPublisher is best-effort by design: it is used by the in-pipeline DomainEventDispatchBehavior<TMessage, TResponse>, which runs after the unit of work has committed and therefore has nothing to retry with — failing the request there would report an error for a write that is already durable. Swallowing is the right behavior in that context.
It is the wrong behavior for a caller that does own a durable retry mechanism. The transactional outbox relay can re-drain a row on a later poll with exponential backoff, so a handler failure there should leave the message pending rather than silently marking it delivered. This contract is that non-swallowing path: the caller receives a DomainEventDispatchReport and decides the retry policy itself.
Implementations must attempt every handler and collect the failures rather than stopping at the first one, so a single failing handler cannot starve its siblings of their side effects. OperationCanceledException matching the supplied token propagates rather than being reported, so the caller can abort cleanly.
Replacing the default IDomainEventPublisher does not replace this contract; a consumer substituting their own dispatch implementation should register both if they use the outbox.
Methods
PublishReportingAsync(IDomainEvent, IReadOnlySet<string>?, CancellationToken)
Publishes the specified domain event to all matching handlers, skipping any the caller has already recorded as complete, and reports the per-handler outcome.
ValueTask<DomainEventDispatchReport> PublishReportingAsync(IDomainEvent domainEvent, IReadOnlySet<string>? completedHandlers, CancellationToken cancellationToken)
Parameters
domainEventIDomainEventThe event to publish. Resolution uses
domainEvent.GetType().completedHandlersIReadOnlySet<string>Handlers (by HandlerIdentity(Type)) that a previous attempt already completed, and that must therefore not be invoked again; pass null on a first attempt. This is what makes a retry re-run only the handlers that actually failed, rather than re-running successful siblings and duplicating their side effects.
cancellationTokenCancellationTokenA token to observe while waiting on the handlers.
Returns
- ValueTask<DomainEventDispatchReport>
A report naming the handlers that are now complete and the ones that failed.