Skip to content

Dispatch Module

The Dispatch module generates strongly-typed command and query dispatch methods from decorated handler methods — zero indirection, method overload resolution IS the dispatch. The generator emits one typed overload per handler, so the C# compiler picks the right one at the call site. Full type safety, no runtime lookup, no mediator pattern.

Quick Start

Define command/query records, write handler methods, and mark a static partial class as the dispatch entry point:

using Deepstaging.Dispatch;
using LanguageExt;

// 1. Define a command
public record CreateOrder(string Name, int Quantity) : ICommand;
public record OrderCreated(string Id);

// 2. Write a handler
public static class OrderCommands
{
    [CommandHandler]
    public static Eff<AppRuntime, OrderCreated> Handle(CreateOrder cmd) =>
        from _ in OrderStore.SaveAsync<AppRuntime>(new Order(cmd.Name, cmd.Quantity))
        select new OrderCreated("123");
}

// 3. Mark the dispatch module
[DispatchModule]
public static partial class AppDispatch;

The generator produces typed dispatch methods on AppDispatch:

// Dispatch via the command record
var result = await AppDispatch.Dispatch(new CreateOrder("Widget", 5))
    .RunAsync(runtime);

// Or use the convenience overload
var result = await AppDispatch.CreateOrder("Widget", 5)
    .RunAsync(runtime);

Features

Feature Description
Typed dispatch One overload per handler — C# compiler resolves the route
Commands & Queries ICommand / IQuery marker interfaces
Auto-commit IAutoCommittable.CommitAsync() called after command handlers (configurable)
Validation Applicative validation with FieldError and structured 422 responses
Authorization [Authorize] on handlers with [AuthPolicies] integration
Idempotency [Idempotent] enforces unique processing per idempotency key
Rate limiting [RateLimit("policy")] applies named rate limit policies to handlers
Convenience overloads AppDispatch.CreateOrder(name, qty) alongside Dispatch(cmd)
Tracing OpenTelemetry activity per dispatch

What's Next

Page Description
Attributes [DispatchModule], [CommandHandler], [QueryHandler], [Authorize], marker interfaces
Generated Code Pipeline steps, result types, convenience overloads, tracing, auditing
Effects Composition How handlers return Eff<RT, T>, runtime constraints, composing with [Runtime] and [Uses]
Validation Applicative validation with FieldError, ValidatorType, and structured 422 responses
Authorization [AuthPolicies], [AuthPolicy], RBAC with [Permissions]/[Roles]/[Require], ASP.NET integration

Diagnostics

ID Severity Description
DSDSP01 Error DispatchModule class must be partial
DSDSP02 Error DispatchModule class must be static
DSDSP03 Error CommandHandler method must be static
DSDSP04 Error QueryHandler method must be static
DSDSP05 Warning CommandHandler has invalid signature (must return Eff<RT, T> with ≥1 parameter)
DSDSP06 Warning QueryHandler has invalid signature (must return Eff<RT, T> with ≥1 parameter)
DSDSP07 Error ValidatorType is missing a Validate method with the correct signature
DSDSP08 Error AuthPolicies class must be static partial
DSDSP09 Error AuthPolicy method must be static bool (ClaimsPrincipal)
DSDSP10 Error Validated = true and ValidatorType are mutually exclusive
DSDSP11 Warning Validated = true but input type has no DataAnnotations or Validate methods
DSDSP12 Error Custom Validate{Field} method returns wrong type
DSDSP13 Info Input type has validated fields without custom validators
DSDSP14 Warning Handler has no [Require], [Authorize], or [Public] attribute