-
Notifications
You must be signed in to change notification settings - Fork 12
Handler Priority
LiteBus provides a mechanism to control the execution order of handlers using the [HandlerPriority]
attribute. This is essential for creating deterministic workflows where certain operations must happen before others.
Handler Priority defines the sequence in which handlers of the same type (e.g., pre-handlers, post-handlers, or event handlers) are executed.
- Lower numbers have higher priority (i.e., they execute first).
- The default priority for any handler without the attribute is
0
. - Handlers with the same priority value are not guaranteed to execute in a specific order relative to each other (unless configured for sequential execution in the Event Module).
This feature replaces the [HandlerOrder]
attribute from versions prior to v4.0.
Apply the [HandlerPriority]
attribute to any handler class.
using LiteBus.Messaging.Abstractions;
[HandlerPriority(1)]
public class MyFirstHandler : ICommandPreHandler<MyCommand>
{
// This will execute first...
}
[HandlerPriority(10)]
public class MySecondHandler : ICommandPreHandler<MyCommand>
{
// This will execute after MyFirstHandler...
}
public class MyDefaultPriorityHandler : ICommandPreHandler<MyCommand>
{
// This has a default priority of 0 and will execute before MyFirstHandler.
}
Priority is commonly used to ensure validation runs before enrichment in a pre-handler chain.
// Priority 1: Validation runs first.
[HandlerPriority(1)]
public class ValidationPreHandler : ICommandPreHandler<CreateUserCommand>
{
public Task PreHandleAsync(CreateUserCommand command, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(command.Email))
{
throw new ValidationException("Email is required.");
}
return Task.CompletedTask;
}
}
// Priority 2: Enrichment runs after successful validation.
[HandlerPriority(2)]
public class EnrichmentPreHandler : ICommandPreHandler<CreateUserCommand>
{
public Task PreHandleAsync(CreateUserCommand command, CancellationToken cancellationToken)
{
// Add a correlation ID to the context for other handlers to use.
AmbientExecutionContext.Current.Items["CorrelationId"] = Guid.NewGuid();
return Task.CompletedTask;
}
}
For events, handlers with the same priority value form a priority group. The EventMediationSettings
control how these groups execute relative to each other.
- By default, priority groups execute sequentially (Group 1 finishes before Group 2 starts).
- This allows you to create phases of event processing.
// Priority 1: Initial data persistence.
[HandlerPriority(1)]
public class SaveToReadModelHandler : IEventHandler<OrderPlacedEvent> { /* ... */ }
// Priority 2: Notifications can only happen after data is saved.
[HandlerPriority(2)]
public class SendEmailToCustomerHandler : IEventHandler<OrderPlacedEvent> { /* ... */ }
[HandlerPriority(2)]
public class NotifyShippingDepartmentHandler : IEventHandler<OrderPlacedEvent> { /* ... */ }
// Priority 3: Analytics runs last.
[HandlerPriority(3)]
public class UpdateAnalyticsDashboardHandler : IEventHandler<OrderPlacedEvent> { /* ... */ }
In the example above, SaveToReadModelHandler
will complete before the two notification handlers begin. The two notification handlers (both priority 2) form a group and will execute based on the HandlersWithinSamePriorityConcurrencyMode
setting.
For more details, see the Event Module documentation.
- Use for Determinism: Only apply priority when a specific execution order is required for correctness (e.g., validation before action).
- Keep Gaps: Leave gaps between priority numbers (e.g., 10, 20, 30) to make it easier to insert new handlers in the future without re-numbering.
- Use Constants: Define priority levels as constants in a shared class to improve readability and avoid magic numbers.
public static class HandlerPriorities
{
public const int Validation = 10;
public const int Enrichment = 20;
public const int Auditing = 100;
}