Portal Community

Guarantee Model

PropertyBehaviour
Delivery modelAt-most-once — events are not replayed after process restart
OrderingEvents are delivered to handlers in publish order within a single call chain
ConcurrencyConcurrent executions publish independently — no cross-execution ordering guarantee
Handler failure isolationIf handler A throws, handler B still runs; publisher is not affected
RetryNo automatic retry — design handlers to be idempotent and self-recovering
PersistenceNone — in-memory only, events lost on process restart

Achieving Durable Delivery

If you need guaranteed delivery (e.g., webhook that must fire even if the app restarts), implement a handler that writes the event to a durable outbox table, then dispatch from the outbox using a background job:

// Handler: write to outbox DB table — this is transactional
public class WorkflowEventOutboxHandler : IWorkflowEventHandler<WorkflowCompleted>
{
    private readonly IOutboxRepository _outbox;

    public WorkflowEventOutboxHandler(IOutboxRepository outbox) => _outbox = outbox;

    public async Task HandleAsync(WorkflowCompleted @event, CancellationToken ct)
    {
        await _outbox.InsertAsync(new OutboxEntry
        {
            Id          = @event.EventId,
            EventType   = "WorkflowCompleted",
            Payload     = JsonSerializer.Serialize(@event),
            CreatedAt   = DateTimeOffset.UtcNow,
            Status      = OutboxStatus.Pending
        }, ct);
    }
}

// Separate background job reads from outbox and delivers to external systems
// — this job survives restarts and can retry failed deliveries

Handler Exception Handling

By default, the dispatcher catches and logs handler exceptions without rethrowing. This means a handler that crashes does not fail the workflow. If you need a handler failure to fail the workflow, throw from the handler and configure the dispatcher to propagate.

// In appsettings.json (or options)
{
  "WorkflowEventBus": {
    "PropagateHandlerExceptions": false,  // default — isolated
    "LogHandlerExceptions": true
  }
}

Process Restart Scenarios

Lost events on restart: If the process crashes between PublishAsync being called and the handlers completing, those events are lost. This is an inherent limitation of the in-process bus. Design your system so that the workflow execution result (written to the database by the engine) is the source of truth, and event handlers are supplementary notification mechanisms.