Migration Guide v6
Greenfield v6 replaces v5 writer APIs and module composition. Current PostgreSQL schemas are inbox/outbox version 3 and saga version 2. This guide covers both the original v6 break from v5 and the v6.0 rename tables for application code that still references legacy identifiers from v5 or early v6 package versions. Shipping libraries in src/ use v6 names exclusively.
Related: API design (suffix taxonomy, CLR kind rules, inbox/outbox Message model), v6 feature index, Dependency graph.
Historical upgrades: Migration Guide v5, Migration Guide v4.
Greenfield v6 (from v5)
Adopt v6 as a fresh integration:
| Area | v5 | v6 |
|---|---|---|
| Target framework | .NET 8/9 | net10.0 only |
| Command scheduling | ICommandScheduler.ScheduleAsync | IInbox.AcceptAsync with InboxAcceptItem |
| Event publication (durable) | IOutboxWriter.AddAsync | IOutbox.EnqueueAsync with OutboxEnqueueItem |
| Module registration | Flat storage/dispatch registrars | Package extensions on ILiteBusBuilder, with nested inbox/outbox adapters |
| PostgreSQL schema | Upgrade scripts from v5 | Current-version greenfield DDL; ordered migrations within v6; no automatic v5 upgrade |
| Processors | Sequential legacy loops | Pipelined processors only |
| Registry | Process-wide MessageRegistry | One registry per IModuleConfiguration |
| Removed | IEventPublisher, IIdempotentCommand, v5 scheduler aliases | See Changelog v6.0.0 |
Writer items use *Metadata for idempotency, visibility, trace, and tenant concerns. InboxOptions, OutboxOptions, IInboxScheduler, and IOutboxScheduler are removed.
Append stores now return InboxAppendResult or OutboxAppendResult from AddAsync and ordered lists of those results from AddBatchAsync. Custom providers must return Accepted / Enqueued only for a new row and AlreadyAccepted / AlreadyEnqueued when either the message ID or tenant-scoped idempotency key resolves an existing row. Writer receipts expose the same axis-specific outcome. The homogeneous IOutbox.EnqueueBatchAsync<TEvent> overload is removed; convert typed items to OutboxEnqueueItem and call the canonical heterogeneous batch overload.
Existing PostgreSQL Databases
New-install scripts contain the current table shape. Existing v6 tables must apply the shipped migrations in order before application startup:
- Inbox and outbox v2: convert
payloadfromjsonbto opaquetext. - Inbox and outbox v3: add
lease_generation bigint NOT NULL DEFAULT 0. - Saga v2: add nullable
last_applied_message_id uuid.
After applying the files exposed by each PostgreSql*Schema.SqlFiles catalog, call EnsureAsync to validate column names and types and to record the current version. EnsureAsync creates a missing current-version table but does not execute migrations against an existing table.
Current create scripts also use a tenant-scoped idempotency unique index on (tenant_id, idempotency_key) where idempotency_key IS NOT NULL. Deployments created before this index shape must run the manual index migration once per table:
- Normalize unscoped rows:
UPDATE <table> SET tenant_id = '' WHERE tenant_id IS NULL. - Drop the legacy single-column idempotency unique index.
- Create the composite unique index on
(tenant_id, idempotency_key)with the same partial filter.
Embedded SQL: Sql/inbox/v1/migrate_tenant_idempotency_index.sql and Sql/outbox/v1/migrate_tenant_idempotency_index.sql. Running ensure_indexes on a new table applies the composite index directly. Coordinate inbox, outbox, and saga changes when the components share one database.
Entity Framework Core model configuration maps the same composite index and lease_generation column. Regenerate or alter EF migrations after upgrading store packages so application-owned migrations match the current shape.
v6.0 Rename Reference
The v6.0 rename pass is complete in shipping packages: src/ contains no legacy identifiers from the tables below. Use the tables when upgrading your solution from v5, from early v6 package versions, or from copied samples that still use legacy names.
Mediation and Handlers
| Legacy (remove from apps) | v6 (keep in libraries) | Notes |
|---|---|---|
MessageMediationRequest with embedded CancellationToken | MessageMediationRequest<TMessage, TResult> | Pass CancellationToken only on IMessageMediator.Mediate(..., cancellationToken) |
MultipleCommandHandlerFoundException | MultipleHandlerFoundException | Consolidated type in LiteBus.Messaging.Abstractions |
MultipleMessageHandlerFoundException | MultipleHandlerFoundException | Same consolidation |
MultipleQueryHandlerFoundException | MultipleHandlerFoundException | Same consolidation |
HandleErrorAsync(message, result, exception, cancellationToken) | HandleErrorAsync(MessageErrorContext<TMessage, TResult>, cancellationToken) | Read failure data from the typed context; set Outcome and optional HandledResult there to recover |
IMessageErrorHandler.HandleError(MessageErrorContext) | IMessageErrorHandler.HandleErrorAsync(MessageErrorContext, CancellationToken) | The runtime base contract is asynchronous and receives cancellation explicitly |
IMessageMediator.MediateAsync<TMessage, TResult>(...) | IMessageMediator.Mediate<TMessage, TResult>(...) | Async strategies use Task or Task<TResult> as TResult; await the returned value directly without creating Task<Task> |
Error-handler contexts default to MessageErrorOutcome.Unhandled, so observation alone does not suppress an exception. Remove explicit implementations of the old synchronous base method and any LegacyErrorHandlerSupport calls. Set context.Outcome = MessageErrorOutcome.Handled only when the handler has recovered; result-bearing handlers also set context.HandledResult.
In-Process Dispatch (Module Builders)
| Legacy | v6 |
|---|---|
UseInProcessDispatcher() | UseInProcessDispatch() |
Both inbox and outbox in-process dispatch packages register through UseInProcessDispatch() on their respective module builders.
Entity Framework Core Storage
| Legacy | v6 |
|---|---|
UseEfCoreStorage(...) | UseEntityFrameworkCoreStorage(...) |
EfCoreInboxStoreOptions / EfCoreOutboxStoreOptions | EntityFrameworkCoreInboxStoreOptions / EntityFrameworkCoreOutboxStoreOptions |
Regenerate application-owned inbox and outbox migrations after updating the EF storage packages. The current model adds IX_LiteBus_Inbox_CreatedAt and IX_LiteBus_Outbox_CreatedAt; MySQL lease queries require these names for ordered skip-locked scans. Existing SQLite tables must convert every durable timestamp column from the provider's DateTimeOffset representation to UTC ticks stored as INTEGER. Rebuilding a SQLite table through an EF migration is the safest conversion path. PostgreSQL and SQL Server timestamp column types do not change.
MySQL and SQLite model mappings do not apply a schema qualifier. MySQL lease SQL uses the current connection database, and SQLite uses the current database file. Keep SchemaName aligned for providers that support schemas, including PostgreSQL and SQL Server.
AWS SQS Packages (NuGet IDs)
| Legacy package ID | v6 package ID |
|---|---|
LiteBus.Transport.AwsSqsSqs | LiteBus.Transport.AwsSqs |
LiteBus.Inbox.Dispatch.AwsSqsSqs | LiteBus.Inbox.Dispatch.AwsSqs |
LiteBus.Outbox.Dispatch.AwsSqsSqs | LiteBus.Outbox.Dispatch.AwsSqs |
LiteBus.Inbox.Ingress.AwsSqsSqs | LiteBus.Inbox.Ingress.AwsSqs |
Extension method names (UseAwsSqsDispatch, UseAwsSqsIngress) are unchanged.
ASP.NET Management Bindings
| Legacy | v6 |
|---|---|
*QueryParameters | *QueryBinding |
*PurgeParameters | *PurgeBinding |
Durable Writer Items (Inbox/Outbox Symmetry)
| Legacy | v6 | Notes |
|---|---|---|
OutboxEnqueueItem.Event | Message | Durable vocabulary is message-centric |
Outbox untyped EventType | MessageType | Aligns with InboxReceipt.MessageType |
InboxAcceptItems / OutboxEnqueueItems | (removed) | Use static factories on InboxAcceptItem / OutboxEnqueueItem |
OutboxEnqueueItem<TEvent> keeps the TEvent type parameter at the CQRS layer.
Event Mediation Settings
| Legacy | v6 |
|---|---|
Nested settings bags on EventMediationSettings | EventRoutingSettings, EventExecutionSettings, EventHandlerFilter |
Saga Module Extensions
| Legacy | v6 |
|---|---|
InboxModuleBuilderExtensions (saga package) | InboxModuleBuilderSagaExtensions |
InboxModuleBuilderExtensions (PostgreSQL saga) | SagaModuleBuilderPostgreSqlExtensions |
Composition and Package Boundaries
| Early v6 surface | Current v6 surface | Migration |
|---|---|---|
AddLiteBus(Action<IModuleRegistry>) | AddLiteBus(Action<ILiteBusBuilder>) | Use package-owned extensions for normal composition and builder.Modules.Register(...) for custom modules. |
builder.Modules.AddMessageModule(...) | builder.AddMessaging(...) | Use package-owned root extensions for normal application composition. |
builder.Modules.AddCommandModule/AddQueryModule/AddEventModule | builder.AddCommands/AddQueries/AddEvents | Keep builder.Modules only for advanced module registry access. |
builder.Modules.AddInboxModule/AddOutboxModule | builder.AddInbox/AddOutbox | Storage, dispatch, and ingress remain nested on their feature builders. |
LiteBus.Orchestration.Abstractions | LiteBus.DurableMessaging.Abstractions | Replace the project or NuGet reference. Shared durable contracts now live in the renamed package. |
IMessageTransport | ITransportPublisher | Consumption remains on IMessageConsumer. |
Module-level ITransportCircuitBreaker | ITransportCircuitBreakerRegistry | Resolve publisher state with GetPublisherCircuit(destination). Ingress does not consume publisher breaker state. |
ThrowIfOpen() plus parameterless breaker outcome methods | AcquirePermit() plus RecordSuccess(permit) or RecordFailure(permit) | Keep the permit for the operation lifetime so stale completions cannot reset a newer breaker generation. |
One TransportConsumerOptions.PrefetchCount used for every broker | MaxInFlightMessages, native PrefetchCount, ReceiveBatchSize, and MaxConcurrentCalls | Use MaxInFlightMessages for the common LiteBus callback cap. Configure only the native fields supported by the selected adapter. |
TransportConsumerOptions.MaxConcurrentMessages | MaxConcurrentCalls | The name now matches Azure Service Bus processor callback semantics. |
AwsSqsInboxIngressOptions.PrefetchCount | ReceiveBatchSize | Use a value from 1 through 10. Invalid values fail during module composition. |
KafkaInboxIngressOptions.PrefetchCount, InMemoryInboxIngressOptions.PrefetchCount | Removed | Neither adapter honored the setting. Use Safety.MaxInFlightMessages for LiteBus callback admission. |
| Unbounded in-memory destination channels | InMemoryTransportOptions.DestinationCapacity, default 1024 | Pass the options to AddInMemoryTransport(...). Publication now waits when queued and in-flight deliveries reach the per-destination limit. |
| AMQP-only broker readiness | AMQP, Kafka, AWS SQS, and Azure Service Bus root transport probes | Set AwsSqsTransportOptions.ConnectivityCheckQueueUrl or AzureServiceBusTransportOptions.ConnectivityCheckTarget. Kafka uses ConnectivityCheckTimeout. Missing SQS or Azure targets report degraded. |
| Fixed host diagnostic runner settings | LiteBusHealthCheckOptions.DiagnosticChecks and LiteBusManagementOptions.DiagnosticChecks | Configure per-probe Timeout and MaxParallelism; defaults are 5 seconds and 4 probes. The health-check registration is tagged litebus and ready. |
Flat ingress MaxMessageBytes, identity, trust, authorization, or batch properties | Nested Safety on every broker ingress options record | Move common settings into TransportInboxIngressSafetyOptions. AMQP trust and batch properties moved under AmqpInboxIngressOptions.Safety too. |
IRegistrableCommandConstruct, IRegistrableQueryConstruct, IRegistrableEventConstruct | Removed | Register actual semantic messages and handlers; no marker replacement is needed. |
Broker options passed to Use*Dispatch or ingress Connection | AddAmqpTransport, AddKafkaTransport, AddAwsSqsTransport, AddAzureServiceBusTransport, or AddInMemoryTransport | Register one shared transport at the root, then configure dispatch and ingress routing only. |
| Implicit root-provider dispatch | Container IMessageDispatchScopeFactory | Use the Microsoft DI or Autofac adapter. Manual hosts must register RootMessageDispatchScopeFactory explicitly. |
EF store resolving a scoped DbContext | IDbContextFactory<TContext> | Register the context with AddDbContextFactory<TContext> before selecting it with UseDbContext<TContext>(). |
| Saga implicit in-memory fallback or flat PostgreSQL registration | Storage selected inside EnableSaga(...) | Call exactly one of UseInMemoryStorage() or UsePostgreSqlStorage(...) on SagaModuleBuilder. |
OutboxEnvelope.AsPublished() | OutboxEnvelope.AsPublished(publishedAt) | Pass the processor clock timestamp explicitly. |
Testing Support Packages
| Legacy | v6 | Notes |
|---|---|---|
FakeCommandMediator, FakeQueryMediator, other mediator Fake* types | Matching Test* names in LiteBus.Testing.Mediation | Reference only semantic mediator abstractions |
Inbox/outbox Fake* stores and processor helpers | Matching Test* names in LiteBus.Testing.DurableMessaging | Reference durable support only when the test needs it |
Transport Fake* types | TestMessageTransport in LiteBus.Testing.Transport | Does not pull broker SDKs or durable packages |
| Host lifecycle helpers | LiteBus.Testing.Hosting | Keeps Generic Host dependencies out of mediator and transport tests |
ManualTimeProvider, LiteBusTestBase | LiteBus.Testing | The base package is framework-neutral |
Not in scope: sample message types in test projects (for example FakeCommand, FakeParentCommand under tests/**/FakeCommand/ folders) are ordinary command fixtures used to exercise the mediation pipeline. They are unrelated to LiteBus.Testing and do not need renaming.
Intentionally Unchanged
SendAsync/PublishAsync/QueryAsyncon semantic mediatorsIOutbox.EnqueueAsyncmethod nameOutboxEnqueueItem<TEvent>type name andTEventtype parameter- Granular package split (inbox vs outbox, per broker, per store)
Use*registration pattern on module builders
Application Migration Checklist
When upgrading a consumer solution, search application code, samples, and copied test helpers (not shipping library sources) for legacy identifiers from the tables above. In particular, replace:
UseInProcessDispatcher(useUseInProcessDispatch)UseEfCoreStorage(useUseEntityFrameworkCoreStorage)FakeCommandMediator,FakeQueryMediator, and other legacy testingFake*doubles (use the matchingTest*type and concern package; do not rename unrelated test message fixtures such asFakeCommand)*QueryParameters/*PurgeParameterson public ASP.NET types (use*Binding)item.Event/ untyped outboxEventType(useMessage/MessageType)MultipleCommandHandlerFoundException,MultipleMessageHandlerFoundException,MultipleQueryHandlerFoundException- NuGet or project references ending in
AwsSqsSqsor*.Awstransport/dispatch/ingress IDs (use*.AwsSqspackage IDs)
Current v6 names such as MessageMediationRequest<TMessage, TResult>, UseInProcessDispatch, UseEntityFrameworkCoreStorage, LeaseRenewalRequest, SagaSaveItem<TState>, MessageErrorContext, and InboxAcceptMetadata.Immediate are expected in shipping libraries.
Run dotnet build on your solution after applying renames.
Next
- Getting started: mediator and durable quickstart
- Inbox / Outbox: writer and processor guides
- API design: suffix taxonomy and CLR kinds