Table of Contents

Migration 2.0.0

2.0.0 is a major release. It collects the breaking changes that could not ship in a minor release, above all the reorganisation of every namespace by feature. This page lists what a consumer of the Bsft Foundation packages changes, in the order a consumer applies it: the .Abstractions namespaces first, then the feature namespaces together with the renames of the naming rules (Naming).

Changed: SagaStepConfigurator takes the builder as a type argument

SagaStepConfigurator<TState, TEvent> held its SagaBuilder<TState>, and the builder created the configurator, so each type depended on the other. The configurator is now SagaStepConfigurator<TState, TEvent, TBuilder>: StartWith, WaitFor and Or return SagaStepConfigurator<TState, TEvent, SagaBuilder<TState>>, and Then, When and WhenNot return TBuilder, which is the same SagaBuilder<TState> as before. A fluent flow definition compiles unchanged.

The builder's definition tracking moved to ISagaDefinitionRegistry (Definitions, Register<TState>), which ISagaBuilderFactory extends, so code that reads ISagaBuilderFactory.Definitions is unchanged as well.

Change Consumer action
SagaStepConfigurator<TState, TEvent> is now SagaStepConfigurator<TState, TEvent, TBuilder> Only code that names the type spells the third argument: SagaStepConfigurator<TState, TEvent, SagaBuilder<TState>>, or use var
ISagaBuilderFactory.Definitions and Register<TState> are declared on ISagaDefinitionRegistry, which ISagaBuilderFactory extends None for callers; an own implementation of ISagaBuilderFactory keeps both members, since it implements both interfaces

Changed: .Abstractions packages declare the namespace of the package they abstract

An .Abstractions package now declares the namespace of the package it abstracts, as Microsoft.Extensions.Logging.Abstractions declares Microsoft.Extensions.Logging: Bsft.Messaging.Abstractions declares namespace Bsft.Messaging, and its Outbox/ folder namespace Bsft.Messaging.Outbox. The seam and the implementation's entry points share one namespace, so a consumer imports one namespace instead of two, and a type can move between the two assemblies without a source break (docs/package-conventions.md, section 3).

This step changes no package or assembly name; Naming renames some packages. .Contracts packages are unchanged. In the Foundation four .Abstractions packages move: Bsft.Correlation, Bsft.Mediator, Bsft.Messaging and Bsft.Outbox, each with .Abstractions as package name and without it as namespace.

Abstractions/ folders inside a non-.Abstractions package (Bsft.Mapping.Abstractions, Bsft.Scheduling.Abstractions, Bsft.Modules.Abstractions and similar) are folder namespaces, not packages. The feature namespaces of the next section dissolve them.

Change Consumer action
namespace Bsft.<X>.Abstractions[.<Folder>] is now namespace Bsft.<X>[.<Folder>] Rewrite the using directives and qualified names
Reflection over namespaces (typeof(T).Namespace, Type.GetType("Bsft.Messaging.Abstractions.IMessageBus, ..."), serialized type names) Update the string; assembly-qualified names keep the assembly name Bsft.Messaging.Abstractions and change only the namespace part

Changed: namespaces follow features (ADR-0013)

Every package now names its namespaces after what the code is for, not after what kind of type it is (ADR-0013, docs/package-conventions.md, sections 2 and 3). The role folders Services, Models, Commands, Queries, Endpoints, Stores, Exceptions, Options, Extensions and the folder namespaces called Abstractions are gone. A type now lives in the namespace of its feature, or in the package root when the whole package speaks it (an options class, an exception every feature throws, the package's central seam). Each namespace equals its folder, which the build enforces with IDE0130.

This step renames no package, type or member: only namespaces move. Naming renames packages, types and members as well. Behavior is unchanged, with the persisted names below and those of the naming section as the only data a consumer has to look at.

Apply this section after the .Abstractions section above.

AddBsft*, MapBsft* and UseBsft* live in <Package>.DependencyInjection

Every package keeps the extension methods that wire it into a host in one namespace, <Package>.DependencyInjection, as Microsoft.Extensions.DependencyInjection and the ASP.NET Core packages do for theirs. In 1.x they sat in the package root or in an Extensions folder. A host therefore imports one namespace per package it wires:

using Bsft.Correlation.DependencyInjection;
using Bsft.Hosting.DependencyInjection;
using Bsft.Mediator.DependencyInjection;

builder.Services.AddBsftCorrelation();
builder.Services.AddBsftDefaults();
builder.Services.AddBsftMediator(mediator => mediator
    .RegisterServicesFromAssembly(typeof(Program).Assembly));

A call the compiler cannot resolve fails with CS1061; add the directive.

Names the framework persists

A few names are derived from a type's full name and outlive the process. 2.0.0 keeps none of the 1.x names; each one below changes with its type.

Name Where it is stored 2.0.0
Logging categories Logging:LogLevel keys and log queries A category is the full name of the logging type, so a filter on an old namespace prefix no longer matches. Filter on the package root (Bsft.Messaging) or the new feature namespace
EF Core model snapshots of a consumer's migrations *ModelSnapshot.cs Entity type names in the snapshot are full names. The next dotnet ef migrations add rewrites them; the generated migration contains no schema operation. Review it and keep it

The same holds for a consumer's own types: an event registered with AddEvent<T>() and no name is published under T's full name, so moving such a type renames the event.

Change Consumer action
Types moved from role namespaces (Services, Models, Commands, Endpoints, Abstractions, ...) to feature namespaces Rewrite the using directives and qualified names; the build names each one
AddBsft*, MapBsft*, UseBsft* moved to <Package>.DependencyInjection using <Package>.DependencyInjection;
Log level filters on removed namespaces Filter on the package root or the new namespace
Reflection over namespaces (typeof(T).Namespace, Type.GetType("..."), serialized type names) Update the string
Own analyzers or conventions that expect Services/Models folders Nothing required: the IDE0130 setting lives in the framework's own .editorconfig and does not reach a consumer. Follow docs/package-conventions.md where you want the same layout

Naming

2.0.0 applies naming.md to every identifier of the framework: two-letter acronyms in capitals (AI, EF, MQ, IP, UI), no abbreviations (Org, Config, Dedup, Seq, Ref, Dest, Spec), American spelling (Canceled, Behavior), one suffix per role (*Validator, *Sweeper, *Reader, *Event, *QueryParameters), the retrieval verbs Get/Find/List by what a method returns, and canonical parameter names. Apply this section together with the feature namespaces above.

Behavior is unchanged. What a consumer stores or sends is listed under Wire and persisted names, each with the step it needs; there is no compatibility shim for any of them (naming.md N7.1, N7.4).

A named argument (AddBsftFoo(config: ...), new Bar(time: ...)) fails with CS1739 and takes the parameter name of naming.md N6.1: cancellationToken, configuration, timeProvider, principal, httpContext, serviceProvider, or the unabbreviated word (organizationUnitId, destination).

Packages

Package ids and assembly names follow the namespace root (naming.md N3.6). NuGet ids compare without case, so the old spelling still restores the package; write the new one so that the id matches the namespace.

1.x 2.0.0
Bsft.EventAggregator.Saga.EfCore Bsft.EventAggregator.Saga.EFCore
Bsft.Idempotency.EfCore Bsft.Idempotency.EFCore
Bsft.Localization.EfCore Bsft.Localization.EFCore
Bsft.Messaging.EfCore Bsft.Messaging.EFCore
Bsft.Messaging.EfCore.Postgres Bsft.Messaging.EFCore.Postgres
Bsft.Messaging.RabbitMq Bsft.Messaging.RabbitMQ
Bsft.Outbox.EfCore Bsft.Outbox.EFCore
Bsft.Scheduling.EfCore Bsft.Scheduling.EFCore

Types renamed

A type whose namespace changed only because a namespace segment was renamed (EfCore → EFCore) is not listed here. These public types changed their simple name; the namespace column is the 2.0.0 one.

Namespace (2.0.0) 1.x 2.0.0
Bsft.Data QueryConfigBuilder QueryConfigurationBuilder
Bsft.Data.MultiTenancy.Sandboxes SandboxTenantSpec SandboxTenantSpecification
Bsft.Data.Postgres CreateFullTextIndexRequest PostgresCreateFullTextIndexRequest
Bsft.Data.Postgres FullTextMigrationBuilderExtensions PostgresFullTextMigrationBuilderExtensions
Bsft.Data.Search IOrgScopedEntity IOrganizationScopedEntity
Bsft.Data.SqlServer CreateFullTextIndexRequest SqlServerCreateFullTextIndexRequest
Bsft.Data.SqlServer FullTextMigrationBuilderExtensions SqlServerFullTextMigrationBuilderExtensions
Bsft.Data.Sqlite CreateFullTextIndexRequest SqliteCreateFullTextIndexRequest
Bsft.Data.Sqlite FullTextMigrationBuilderExtensions SqliteFullTextMigrationBuilderExtensions
Bsft.EventAggregator.Saga.EFCore EfCoreSagaInstanceReader EFCoreSagaInstanceReader
Bsft.EventAggregator.Saga.EFCore EfCoreSagaPersistenceWindow EFCoreSagaPersistenceWindow
Bsft.EventAggregator.Saga.EFCore EfCoreSagaStore EFCoreSagaStore
Bsft.EventAggregator.Saga.EFCore EfCoreSagaTimeoutReader EFCoreSagaTimeoutReader
Bsft.EventAggregator.Saga.EFCore EfCoreSagaTimeoutStore EFCoreSagaTimeoutStore
Bsft.EventAggregator.Saga.EFCore IEfCoreSagaStore IEFCoreSagaStore
Bsft.EventAggregator.Saga.EFCore IEfCoreSagaTimeoutStore IEFCoreSagaTimeoutStore
Bsft.EventAggregator.Saga.EFCore.DependencyInjection SagaEfCoreServiceCollectionExtensions SagaEFCoreServiceCollectionExtensions
Bsft.Hosting.DependencyInjection LoggingServiceCollectionExtensions HostLoggingServiceCollectionExtensions
Bsft.Idempotency.EFCore.DependencyInjection IdempotencyEfCoreServiceCollectionExtensions IdempotencyEFCoreServiceCollectionExtensions
Bsft.Localization.EFCore.DependencyInjection LocalizationEfCoreServiceCollectionExtensions LocalizationEFCoreServiceCollectionExtensions
Bsft.Logging.LogFiles LogReadQuery LogReadRequest
Bsft.Mediator Mediator DefaultMediator
Bsft.Messaging.EFCore.DependencyInjection MessagingEfCoreServiceCollectionExtensions MessagingEFCoreServiceCollectionExtensions
Bsft.Messaging.EFCore.Inbox InboxPruner InboxSweeper
Bsft.Messaging.RabbitMQ BsftRabbitMqOptions BsftRabbitMQOptions
Bsft.Messaging.RabbitMQ RabbitMqMessageFormatException RabbitMQMessageFormatException
Bsft.Messaging.RabbitMQ RabbitMqQueueType RabbitMQQueueType
Bsft.Messaging.RabbitMQ.DependencyInjection RabbitMqServiceCollectionExtensions RabbitMQServiceCollectionExtensions
Bsft.Outbox.EFCore EfCoreOutboxStore EFCoreOutboxStore
Bsft.Scheduling.EFCore.DependencyInjection SchedulingEfCoreServiceCollectionExtensions SchedulingEFCoreServiceCollectionExtensions

Members renamed

The public members (methods, properties, constants and enum values) whose name changed, by the declaring type's 2.0.0 name. Implementations and overrides follow their interface or base member and are not repeated.

Type (2.0.0) 1.x 2.0.0
Bsft.Caching.Invalidation.CacheTagIndex GetKeys ListKeys
Bsft.Caching.Invalidation.ICacheTagIndex GetKeys ListKeys
Bsft.Core.Polling.IPollReporter ReportPassCancelledWithoutStop ReportPassCanceledWithoutStop
Bsft.Data.Contracts.Pagination.SortItem Dir Direction
Bsft.Data.Search.IOrganizationScopedEntity OrgUnitId OrganizationUnitId
Bsft.Data.Search.SearchScopeState AllowedOrgUnitIds AllowedOrganizationUnitIds
Bsft.EventAggregator.Saga.Diagnostics.ISagaTelemetry TimeoutCancelled TimeoutCanceled
Bsft.EventAggregator.Saga.EFCore.DependencyInjection.SagaEFCoreServiceCollectionExtensions AddBsftSagaEfCore AddBsftSagaEFCore
Bsft.EventAggregator.Saga.EFCore.DependencyInjection.SagaEFCoreServiceCollectionExtensions AddBsftSagaEfCoreWithTimeouts AddBsftSagaEFCoreWithTimeouts
Bsft.EventAggregator.Saga.EFCore.EFCoreSagaStore GetAsync FindAsync
Bsft.EventAggregator.Saga.EFCore.EFCoreSagaStore RemoveAsync DeleteAsync
Bsft.EventAggregator.Saga.EFCore.EFCoreSagaTimeoutStore GetDueAsync ListDueAsync
Bsft.EventAggregator.Saga.ISagaStore GetAsync FindAsync
Bsft.EventAggregator.Saga.ISagaStore RemoveAsync DeleteAsync
Bsft.EventAggregator.Saga.InMemorySagaStore GetAsync FindAsync
Bsft.EventAggregator.Saga.InMemorySagaStore RemoveAsync DeleteAsync
Bsft.EventAggregator.Saga.Timeouts.ISagaTimeoutStore GetDueAsync ListDueAsync
Bsft.EventAggregator.Saga.Timeouts.InMemorySagaTimeoutStore GetDueAsync ListDueAsync
Bsft.Idempotency.EFCore.DependencyInjection.IdempotencyEFCoreServiceCollectionExtensions AddBsftIdempotencyEfCore AddBsftIdempotencyEFCore
Bsft.Localization.EFCore.DependencyInjection.LocalizationEFCoreServiceCollectionExtensions AddBsftLocalizationEfCore AddBsftLocalizationEFCore
Bsft.Localization.ITranslationReader GetForKey ListForKey
Bsft.Localization.ITranslationReader GetForLocale ListForLocale
Bsft.Localization.ITranslationReader GetForLocaleOrFallback ListForLocaleOrFallback
Bsft.Localization.ITranslationReader GetLocales ListLocales
Bsft.Mediator.IRequestMetadata GetAttribute FindAttribute
Bsft.Mediator.IRequestMetadata GetAttributes ListAttributes
Bsft.Mediator.MediatorDefaults OrgAuthorization OrganizationAuthorization
Bsft.Messaging.EFCore.DependencyInjection.MessagingEFCoreServiceCollectionExtensions AddBsftInboxEfCore AddBsftInboxEFCore
Bsft.Messaging.EFCore.DependencyInjection.MessagingEFCoreServiceCollectionExtensions AddBsftMessagingEfCore AddBsftMessagingEFCore
Bsft.Messaging.EFCore.Postgres.DependencyInjection.PostgresServiceCollectionExtensions AddBsftMessagingEfCorePostgres AddBsftMessagingEFCorePostgres
Bsft.Messaging.RabbitMQ.DependencyInjection.RabbitMQServiceCollectionExtensions AddBsftRabbitMqMessaging AddBsftRabbitMQMessaging
Bsft.Modules.IModuleCatalog GetModules ListModules
Bsft.Modules.IModuleHost GetModuleInfo ListModuleInfo
Bsft.Modules.ModuleCatalog FromConfigFile FromConfigurationFile
Bsft.Modules.ModuleCatalog GetModules ListModules
Bsft.Modules.ModuleHost GetModuleInfo ListModuleInfo
Bsft.Modules.ModulesConfiguration FromConfigFile FromConfigurationFile
Bsft.Scheduling.CronExpression GetNextOccurrence FindNextOccurrence
Bsft.Scheduling.EFCore.DependencyInjection.SchedulingEFCoreServiceCollectionExtensions AddBsftSchedulingEfCore AddBsftSchedulingEFCore
Bsft.Scheduling.EFCore.DependencyInjection.SchedulingEFCoreServiceCollectionExtensions AddBsftStoredSchedulesEfCore AddBsftStoredSchedulesEFCore
Bsft.Scheduling.IJobStore GetAsync FindAsync
Bsft.Scheduling.IJobStore GetClaimableAsync ListClaimableAsync
Bsft.Scheduling.InMemoryJobStore GetAsync FindAsync
Bsft.Scheduling.InMemoryJobStore GetClaimableAsync ListClaimableAsync
Bsft.Scheduling.JobSchedule GetNextOccurrence FindNextOccurrence
Bsft.Scheduling.StoredSchedules.IStoredScheduleService GetAsync FindAsync
Bsft.Scheduling.StoredSchedules.IStoredScheduleStore GetAsync FindAsync
Bsft.Scheduling.StoredSchedules.IStoredScheduleStore GetByTenantAsync ListByTenantAsync
Bsft.Scheduling.StoredSchedules.IStoredScheduleStore GetDueAsync ListDueAsync
Bsft.Scheduling.StoredSchedules.InMemoryStoredScheduleStore GetAsync FindAsync
Bsft.Scheduling.StoredSchedules.InMemoryStoredScheduleStore GetByTenantAsync ListByTenantAsync
Bsft.Scheduling.StoredSchedules.InMemoryStoredScheduleStore GetDueAsync ListDueAsync
Bsft.Testing.Sagas.ISagaTestHarness GetPendingTimeoutsAsync ListPendingTimeoutsAsync
Bsft.Testing.Sagas.ISagaTestHarness GetSagaAsync FindSagaAsync
Bsft.Testing.Sagas.SagaTestHarness GetPendingTimeoutsAsync ListPendingTimeoutsAsync
Bsft.Testing.Sagas.SagaTestHarness GetSagaAsync FindSagaAsync

Removed and consolidated types

  • RequiredPermissionsMetadata was declared once per package that maps routes. It is now one record, Bsft.Http.RequiredPermissionsMetadata, with the same shape; code that looked for several copies by type now looks for one.

Wire and persisted names

A name derived from a C# name follows it (naming.md N7.1), and in 2.0.0 a name written out as a contract of its own follows the glossary as well (naming.md N7.4). Everything below changed, with the step it needs.

Name Where it lives 1.x → 2.0.0 Consumer action
Package ids PackageReference, PackageVersion Table Packages Update the ids
Namespaces and type names, hence logging categories Logging:LogLevel keys, log queries, dashboards *.EfCore → *.EFCore, Bsft.Messaging.RabbitMq → Bsft.Messaging.RabbitMQ, *Pruner → *Sweeper and the other type renames A case-only change needs nothing where the filter compares without case (Logging:LogLevel does); rename filters naming a renamed type
Metric bsft.saga.timeout.cancelled Metrics backends → bsft.saga.timeout.canceled Update dashboards and alerts
Configuration sections of the EF Core and RabbitMQ packages appsettings*.json, environment variables …:EfCore → …:EFCore, Bsft:RabbitMq → Bsft:RabbitMQ None: configuration keys compare without case
Mediator behavior name Behavior ordering configuration, DescribeKnownNames OrgAuthorization → OrganizationAuthorization Rename it where a host names the behavior
Sort direction Query key of PagedRequest.Sort Sort[i].Dir → Sort[i].Direction (SortItem.Dir → Direction) Update clients that sort
Log properties Structured log sinks, log queries, dashboards, alerts PrunedCount → PurgedCount (every retention sweeper's summary line, which now reads "Purged …") Update queries and alerts that match the property names or the message text
EF Core model snapshots *ModelSnapshot.cs Entity type names are full names The next migration rewrites them
Change Consumer action
Packages and types renamed Build; the package and type tables give each new name
Members renamed Build; the member table gives each new name
Parameters renamed Only named arguments break (CS1739); use the canonical name
Wire and persisted names The table above, row by row, before the first 2.0.0 instance starts