Package Guide
55,000+ automated tests | 197 NuGet packages | 10-stage CI pipeline | PublicAPI analyzer on every package | .NET 10.0
Yes, you can use Excalibur.Dispatch by itself. It's a complete messaging framework -- no event sourcing, domain modeling, or compliance packages required. See Dispatch Only for a focused guide.
Excalibur is one framework with focused package families. Install only what your application needs -- every package beyond Excalibur.Dispatch is optional.
Quick Summary
| Package Family | Purpose | When to Add |
|---|---|---|
Excalibur.Dispatch.* | Messaging, pipeline, handlers, transports | Always -- this is the foundation |
Excalibur.Dispatch.{RabbitMQ,Kafka,Azure,Aws} | Experience metapackages bundling transport + resilience + observability | When you want one-line transport setup |
Excalibur.Domain | Aggregates, entities, value objects | When you need rich domain modeling |
Excalibur.EventSourcing.* | Event stores, snapshots, persistence | When you need event sourcing |
Excalibur.Saga.* | Sagas and process managers | When you need long-running workflows |
Excalibur.Hosting.* | ASP.NET Core, serverless hosting | When you need opinionated hosting templates |
Excalibur.LeaderElection.* | Distributed coordination | When you need single-leader guarantees |
Excalibur.SqlServer, Excalibur.Postgres | Full-stack database metapackages: event sourcing + outbox + inbox + sagas + leader election + audit + compliance | When you want one-line database setup |
All packages share the Excalibur.* namespace. You never rewrite existing code when adding new capabilities -- just install additional packages.
Capabilities at a Glance
What the framework provides, by area. Use this to decide what you need; the sections below map each need onto the packages that supply it.
Messaging and transports
- Six transport providers — Kafka, RabbitMQ, Azure Service Bus, AWS SQS, Google Pub/Sub, and in-memory — plus MQTT, IBM MQ, and Apache Pulsar primitives.
- Multi-transport routing — route different message types to different brokers in one application.
- A focused transport API —
ITransportSender,ITransportReceiver, andITransportSubscriberwith a decorator chain and builder pattern.
Reliability
- Outbox — at-least-once delivery with per-partition ordering, retry backoff, and a terminal dead-letter state.
- Inbox and idempotency — provider-native transactional processing where the store supports it, and a durable at-most-once guard where it does not.
- Dead-letter queue — universal across transports, with configurable retry.
- Sagas — event-driven coordination with optimistic concurrency, declarative timeouts, and retention purge on every store.
- Durable execution — replayable workflows whose progress survives process restarts.
- Polly v8 resilience — circuit breaker, retry, timeout, bulkhead, and graceful degradation.
- Leader election — single-leader guarantees with strictly monotonic fencing tokens on every backend.
Event sourcing
- Event stores across relational, document, and cloud providers, with optimistic concurrency.
- Snapshots — time-based, count-based, and hybrid policies with version upgrading.
- Event upcasting — schema evolution through type-safe transformers.
- Projections — inline and asynchronous, with checkpointing, rebuilds, and server-side query filters.
Security and compliance
- Message encryption — AES-256-GCM envelope encryption with pluggable key providers (Azure Key Vault, AWS KMS, HashiCorp Vault).
- Asymmetric message signing — ECDSA P-256 for verifiable message integrity.
- PII-safe telemetry — sanitization with hashed or keyed fingerprints, so identifiers do not leak into traces and metrics.
- GDPR — right-to-erasure with a coverage gate, per-subject crypto-shredding, legal hold, and a data inventory.
- Compliance mapping — FedRAMP, SOC 2, HIPAA, and GDPR checklists mapped to framework capabilities, plus SBOM generation and regulatory key escrow.
Operations and observability
- OpenTelemetry native —
ActivitySourceandMeterinstrumentation across packages, with W3C and B3 context propagation. - Health checks — readiness and liveness probes for transports, event stores, and background services.
- An operational dashboard — live state for outbox, dead-letter, inbox, saga, projection lag, and leader election.
- Audit logging — SIEM integration with Datadog, Splunk, and Microsoft Sentinel exporters.
Developer experience
- Native AOT — 160 of 195 shipped projects declare AOT compatibility; the remainder are blocked by external SDK dependencies rather than by framework code.
- Roslyn analyzers for common mistakes, and source generators for AOT-compatible registration, serialization, and saga coordination.
dotnet newtemplates —excalibur-dispatch,excalibur-eventsourcing,excalibur-saga.- Startup validation everywhere —
Add*registrations validate their options at startup, so a misconfiguration surfaces before the first request rather than at first use.
Decision Flowchart
Scenario-Based Package Selection
Dispatch-Only Scenarios
These scenarios use Excalibur.Dispatch alone -- no domain modeling, event sourcing, or sagas required.
| If You're Building... | Install These |
|---|---|
| Simple API with handlers (MediatR replacement) | Excalibur.Dispatch |
| Message-driven microservices with Kafka | + Excalibur.Dispatch.Transport.Kafka |
| Azure Functions serverless | Excalibur.Dispatch.Hosting.AzureFunctions |
| AWS Lambda serverless | Excalibur.Dispatch.Hosting.AwsLambda |
| Production ASP.NET Core app | + Excalibur.Hosting.Web |
Extended Scenarios
These scenarios add domain modeling, persistence, or compliance packages on top of Dispatch.
| If You're Building... | Install These |
|---|---|
| DDD application with aggregates | + Excalibur.Domain |
| Event-sourced system | + Excalibur.EventSourcing, Excalibur.EventSourcing.SqlServer |
| CQRS with projections | + Excalibur.Caching |
| Long-running workflows | + Excalibur.Saga, Excalibur.Saga.SqlServer |
| Working toward SOC 2 / GDPR — see the note below | + Excalibur.Compliance.*, Excalibur.AuditLogging.* |
They provide building blocks — consent and erasure stores, audit logging, retention — that a compliance programme can be built on. Compliance is a property of your whole system and your processes, not of a dependency list: what you collect, how you scope it per tenant, how you provision the schema, how you retain and erase it, and what you can evidence to an auditor. Some of these stores also require schema you provision yourself, and that provisioning is not shipped for every table.
See the compliance disclaimer before relying on any of this for a regulatory obligation.
Experience Metapackages
For the fastest setup, use the experience metapackages. Each bundles a transport with Polly resilience and OpenTelemetry observability into a single AddDispatch*() call:
| Metapackage | Method | Includes |
|---|---|---|
Excalibur.Dispatch.RabbitMQ | AddDispatchRabbitMQ() | RabbitMQ transport + Polly resilience + OpenTelemetry observability |
Excalibur.Dispatch.Kafka | AddDispatchKafka() | Kafka transport + Polly resilience + OpenTelemetry observability |
Excalibur.Dispatch.Azure | AddDispatchAzure() | Azure Service Bus transport + Polly resilience + OpenTelemetry observability |
Excalibur.Dispatch.Aws | AddDispatchAws() | AWS SQS transport + Polly resilience + OpenTelemetry observability |
// One line: transport + resilience + observability
services.AddDispatchRabbitMQ(rmq =>
{
rmq.ConnectionString("amqp://guest:guest@localhost:5672/");
});
Each method also accepts an optional Action<IDispatchBuilder> for additional pipeline configuration (handlers, caching, validation, etc.):
services.AddDispatchKafka(
kafka =>
{
kafka.BootstrapServers("localhost:9092")
.ConfigureConsumer(c => c.GroupId("order-service"));
},
dispatch =>
{
dispatch.AddHandlersFromAssembly(typeof(Program).Assembly);
dispatch.UseCaching();
});
If you need fine-grained control over which features are included, use the individual packages (Excalibur.Dispatch.Transport.RabbitMQ, etc.) with explicit AddDispatch() builder calls instead.
Full-Stack Database Metapackages
For production deployments, the full-stack database metapackages bundle event sourcing, outbox, inbox, sagas, leader election, audit logging, compliance, and data access into a single AddExcalibur*() call:
| Metapackage | Method | Includes |
|---|---|---|
Excalibur.SqlServer | AddExcaliburSqlServer() | EventSourcing + Outbox + Inbox + Sagas + Leader Election + Audit Logging + Compliance + Data Access (SQL Server) |
Excalibur.Postgres | AddExcaliburPostgres() | EventSourcing + Outbox + Inbox + Sagas + Leader Election + Audit Logging + Compliance + Data Access (PostgreSQL) |
// One line: complete SQL Server stack
services.AddExcaliburSqlServer(sql =>
{
sql.ConnectionString = connectionString;
sql.UseLeaderElection = true; // default: true
sql.UseAuditLogging = true; // default: true
sql.UseCompliance = true; // default: true
});
// Or PostgreSQL
services.AddExcaliburPostgres(pg =>
{
pg.ConnectionString = connectionString;
pg.UseLeaderElection = true;
pg.UseAuditLogging = true;
});
All features default to enabled. Disable individual features by setting the corresponding Use* property to false.
See the Pick Your Stack guide for scenario-based package selection.
Hosting Packages
| Deployment Model | Package | Notes |
|---|---|---|
| Console App | Excalibur.Dispatch | Minimal |
| ASP.NET Core | Excalibur.Dispatch.Hosting.AspNetCore + Excalibur.Hosting.Web | Full hosting |
| Worker Service | Excalibur.Dispatch + Excalibur.Hosting | Background jobs |
| Azure Functions | Excalibur.Dispatch.Hosting.AzureFunctions | Serverless |
| AWS Lambda | Excalibur.Dispatch.Hosting.AwsLambda | Serverless |
| Google Cloud Functions | Excalibur.Dispatch.Hosting.GoogleCloudFunctions | Serverless |
Compliance & Audit Packages
| Package | Purpose |
|---|---|
Excalibur.Compliance.* | Compliance scanning, audit trail |
Excalibur.AuditLogging.Datadog | Datadog SIEM integration |
Excalibur.AuditLogging.Sentinel | Microsoft Sentinel integration |
Excalibur.AuditLogging.Splunk | Splunk integration |
Excalibur.Compliance.SqlServer | Key escrow persistence |
Gradual Adoption Path
Phase 1: Messaging (MediatR Replacement)
dotnet add package Excalibur.Dispatch
// Program.cs -- auto-discovers handlers from the entry assembly
services.AddDispatch();
// Define action
public record CreateOrderAction(string CustomerId, decimal Amount) : IDispatchAction;
// Define handler
public class CreateOrderHandler : IActionHandler<CreateOrderAction>
{
public async Task HandleAsync(CreateOrderAction action, CancellationToken ct)
{
// Business logic
}
}
// Dispatch
await dispatcher.DispatchAsync(new CreateOrderAction("cust-123", 99.99m), ct);
This alone replaces MediatR.
Phase 2: Add Domain Modeling
When you need rich domain models with aggregates:
dotnet add package Excalibur.Domain
public class Order : AggregateRoot<Guid>
{
public OrderStatus Status { get; private set; }
public static Order Create(string customerId, decimal amount)
{
var order = new Order();
order.RaiseEvent(new OrderCreatedEvent(Guid.NewGuid(), customerId, amount));
return order;
}
public void Confirm()
{
if (Status != OrderStatus.Pending)
throw new InvalidOperationException("Order must be pending");
RaiseEvent(new OrderConfirmedEvent(Id));
}
protected override bool ApplyEventInternal(IDomainEvent @event) => @event switch
{
OrderCreatedEvent e => ApplyEvent(e),
OrderConfirmedEvent e => ApplyEvent(e),
_ => false
};
private bool ApplyEvent(OrderCreatedEvent e) { Id = e.OrderId; Status = OrderStatus.Pending; return true; }
private bool ApplyEvent(OrderConfirmedEvent e) { Status = OrderStatus.Confirmed; return true; }
}
Phase 3: Add Event Sourcing
When you need to persist events and rebuild state:
dotnet add package Excalibur.EventSourcing
dotnet add package Excalibur.EventSourcing.SqlServer
dotnet add package Excalibur.Hosting
// Program.cs -- sensible defaults
services.AddExcalibur(excalibur =>
{
excalibur.AddEventSourcing(es => es.UseEventStore<SqlServerEventStore>());
});
Need transports or custom pipelines? Call AddDispatch with a builder action:
// Program.cs -- with custom messaging configuration
services.AddDispatch(dispatch =>
{
dispatch.AddHandlersFromAssembly(typeof(Program).Assembly);
dispatch.UseRabbitMQ(rmq => rmq.HostName("localhost"));
});
services.AddExcalibur(excalibur =>
{
excalibur.AddEventSourcing(es => es.UseEventStore<SqlServerEventStore>());
});
// In handler
public class ConfirmOrderHandler : IActionHandler<ConfirmOrderAction>
{
private readonly IEventSourcedRepository<Order> _repository;
public ConfirmOrderHandler(IEventSourcedRepository<Order> repository)
{
_repository = repository;
}
public async Task HandleAsync(ConfirmOrderAction action, CancellationToken ct)
{
var order = await _repository.GetByIdAsync(action.OrderId, ct);
order.Confirm();
await _repository.SaveAsync(order, ct);
}
}
Phase 4: Full Stack
Add sagas, hosting templates, and compliance as needed:
dotnet add package Excalibur.Hosting.Web
dotnet add package Excalibur.Saga
dotnet add package Excalibur.Saga.SqlServer
dotnet add package Excalibur.Outbox
dotnet add package Excalibur.LeaderElection
AddExcalibur() remains the unified entry point, but each feature method comes from its feature package (for example, .AddOutbox(...) requires Excalibur.Outbox).
services.AddDispatch(dispatch =>
{
dispatch.AddHandlersFromAssembly(typeof(Program).Assembly);
dispatch.UseRabbitMQ(rmq => rmq.HostName("localhost"));
dispatch.UseObservability();
dispatch.UseResilience(res => res.DefaultRetryCount = 3);
});
services.AddExcalibur(excalibur =>
{
excalibur
.AddEventSourcing(es => es.UseEventStore<SqlServerEventStore>())
.AddOutbox(outbox => outbox.UseSqlServer(opts => opts.ConnectionString = connectionString))
.AddSagas(opts => opts.EnableTimeouts = true)
.AddLeaderElection(opts => opts.LeaseDuration = TimeSpan.FromSeconds(30));
});
Package Ownership by Family
Excalibur.Dispatch (Messaging)
| Category | Package | Examples |
|---|---|---|
| Message dispatching | Excalibur.Dispatch | IDispatcher, DispatchAsync() |
| Message contracts | Excalibur.Dispatch.Abstractions | IDomainEvent, IDispatchAction |
| Pipeline | Excalibur.Dispatch | IDispatchMiddleware |
| Handlers | Excalibur.Dispatch.Abstractions | IActionHandler<T>, IEventHandler<T> |
| Context | Excalibur.Dispatch | MessageContext, correlation |
| Serialization | Excalibur.Dispatch.Serialization.* | MemoryPack, MessagePack |
| Transports | Excalibur.Dispatch.Transport.* | Kafka, RabbitMQ, Azure Service Bus |
| Experience metapackages | Excalibur.Dispatch.{RabbitMQ,Kafka,Azure,Aws} | One-line transport + resilience + observability |
| Observability | Excalibur.Dispatch.Observability | Metrics, tracing |
| Compliance | Excalibur.Compliance.* | Audit logging, SIEM |
Excalibur (Domain & Persistence)
| Category | Package | Examples |
|---|---|---|
| Domain modeling | Excalibur.Domain | AggregateRoot<T>, Entity<T> |
| Event sourcing | Excalibur.EventSourcing | IEventStore, ISnapshotStore |
| SQL Server persistence | Excalibur.EventSourcing.SqlServer | Event store implementation |
| Data access | Excalibur.Data.Abstractions | IDataRequest, IDb |
| Sagas | Excalibur.Saga.* | Process managers, orchestration |
| Hosting | Excalibur.Hosting.* | Web, Worker templates |
| Leader election | Excalibur.LeaderElection.* | Distributed coordination |
| Caching | Excalibur.Caching | Projection invalidation |
| Full-stack metapackages | Excalibur.SqlServer, Excalibur.Postgres | AddExcaliburSqlServer(), AddExcaliburPostgres() |
Common Questions
Can I use Excalibur.Dispatch without the other packages?
Yes! Excalibur.Dispatch is completely standalone. Use it as a MediatR replacement with zero additional dependencies.
Do I need multiple packages for event sourcing?
Yes. Excalibur.Dispatch provides the message contracts (IDomainEvent), Excalibur.Domain provides aggregates, and Excalibur.EventSourcing provides the persistence (IEventStore).
Which hosting package for serverless?
Use the serverless hosting packages (Excalibur.Dispatch.Hosting.AzureFunctions, Excalibur.Dispatch.Hosting.AwsLambda). They don't need the full hosting stack.
Where do compliance features live?
In the Excalibur.Compliance.* and Excalibur.AuditLogging.* packages.
What are the experience metapackages?
The Excalibur.Dispatch.RabbitMQ, .Kafka, .Azure, and .Aws packages are convenience metapackages that bundle a transport with Polly resilience and OpenTelemetry observability. Use them for the fastest setup; use the individual transport packages for fine-grained control.
See Also
- Getting Started - Quick start tutorial
- Handlers - Handler patterns and best practices
- Event Sourcing - Event store patterns
- Architecture Overview - Package architecture and boundaries
- Patterns - Outbox, Inbox, and more