From v1 to v2

Version 2 contains a number of breaking changes from version 1. The core logic and concepts all remain the same in Nexus and this release is mostly about code and project reorganization, removing unnecessary dependencies and updating to .NET 8.

New NuGet package structure

Instead of having the different sub systems such as CommerceMind.Nexus.Jobs in their own NuGet packages they have been consolidated into the new CommerceMind.Nexus package and the abstraction packages have been consolidated into CommerceMind.Nexus.Abstractions.

This reorganization means that many interfaces have been moved to new namespaces. Eg IScheduledJob which now lives in CommerceMind.Nexus.Abstractions.Jobs instead of CommerceMind.Nexus.Jobs.Abstractions. The interface names are still the same, so it should be easy to either search-and-replace the namespaces or let Visual Studio/Rider/etc guide you to the new names.

Another reorganization change in this is that the implementations for the different databases are no longer included in the main packages. Instead they have been extracted into CommerceMind.Nexus.Postgres, CommerceMind.Nexus.SqlServer, and CommerceMind.Nexus.Sqlite. Allowing you to only include the database(s) you want.

New fluent service registration

In v1 there was a number of extension methods on IServiceCollection that you had to call in a specific order. In v2 this has changed to a fluent registration on IServiceCollection where the order of which method you call is no longer important.

This is how the v2 registration looks like:

    // Don't forget this one!

Note that you can still split up your service registration as long as the call to .Build() is done last, as that will register additional services based on your configuration. You can for example do this:


Each of these lines could be in different projects as well, as long as the call to Build() happens last. All calls to builder.Services.AddNexus() will return the same Nexus registration object for that specific service collection. It's also safe to call any method on the Nexus registration object multiple times.

Nexus no longer uses Newtonsoft.Json by default

Instead System.Text.Json is used by default and the Newtonsoft.Json serializers have been extracted into a separate package called CommerceMind.NewtonsoftJson which you can use by calling builder.Services.AddNexus().AddNewtonsoftJson().

You probably don't need Newtonsoft.Json, this is only if you've modified how the JSON in Nexus is serialized with custom converters.

A notable change here is that the JSON formatting rules for ASP.NET will no longer be applied for queue messages in the API. Instead the JSON formatting rules for Nexus is applied, which means that you might get a response looking like this:

  "id": 4823771,
  "status": "Processed",
  "retryCount": 0,
  "message": {
    "Id": null,
    "SomeProperty": null,
    "ExampleEnum": "Value1"

Where the outer object have gotten kebabCase properties but the message object has PascalCase properties. You can control both of these (see docs about JSON) formatting options.

Nexus now requires >= .NET 8

v1 supported .NET 6 but v2 now reqires at least .NET 8. The only thing you need to do is to upgrade your .NET version in your application if you haven't already done so.

Deprecated methods and properties have been removed

Methods and properties that have been deprecated in v1 have been removed in v2. If you upgrade to v1.23.0 and fix all build warnings about obsolete methods and properties you'll automatically be prepared for the removals of them in v2.

Default job history retention is now 30 days

Previously the default was to keep job history in the database forever. To avoid having Nexus growing your database indefinitely the default has been changed to 30 days. You can still configure this just like before, but since you can now have different retention per job the property is now called DefaultHistoricalRunsRetention:

builder.Services.AddNexus().AddScheduledJobs(options =>
    options.DefaultHistoricalRunsRetention = TimeSpan.FromDays(10);

You set it per job with the [ScheduledJob] attribute like this:

[ScheduledJob("MyJob", HistoricalRunsRetention = "30.00:00")]
public class MyJob : IScheduledJob

The IEnqueuer and IEnqueuer interfaces have been simplified

Previously IEnqueuer<T> inherited from IEnqueuer which it no longer does. In v1 every message type was registered as a IEnqueuer which means that you could get all enqueuers by requesting IEnumerable<IEnqueuer> like this:

public class DynamicEnqueuer(IEnumerable<IEnqueuer> enqueuers)
    public Task EnqueueAsync(IQueueMessage message)
        var enqueuer = enqueuers.Single(e => e.MessageType == message.GetType());
        return enqueuer.EnqueueAsync(message);

In v2 there is only a single non-generic IEnqueuer registered which does the above for you. Meaning that you can pass a IQueueMessage message to it and it will figure out which concrete IEnqueuer<T> to pass it on to.

Both the IEnqueuer<T> and IEnqueuer interfaces have been simplified to take a message and an EnqueueContext instead of having multiple different overloads for when you want to specify a custom status, a date for when to process it, etc.

So when you did this in v1:

await enqueuer.EnqueueAsync(message, DateTime.UtcNow.AddHours(1));

To enqueue a message to be processed in one hour you now do this:

await enqueuer.EnqueueAsync(message, new EnqueueContext { ProcessAfter = TimeSpan.FromHours(1) });

No longer using Serilog

Nexus previously used Serilog to capture logs happening inside job runs. With v2 it instead has a ILoggerProvider which is registered by the scheduled job system.

In v2 you can use any logging framework you want as long as it writes to external logging providers. Note that this is turned off by default in Serilog and that you need to enable it, eg like this:

builder.Host.UseSerilog((context, services, configuration) =>

// This one is important
}, writeToProviders: true);

If you've used the jobs logging in v2 you also need to remove the Nexus Serilog sink from your configuration:

"Serilog": {
  "Using": ["CommerceMind.Nexus.Jobs"],
  "WriteTo": [{ "Name": "NexusJobLogs" }]

Changes to the IJobLogProvider interface

For consistencys sake the IJobLogProvider interface has been renamed to IJobLogReader since there's also a IJobLogWriter interface.

And instead of just getting a date to fetch logs newer than Nexus now passes in a date range. You also get the log levels passed as LogLevel enums rather than strings.

Also note that JobLogEvent.Level has changed data type from string to the LogLevel enum.